http://www.perlmonks.org?node_id=996206


in reply to Re^3: Parsing SOAP::Lite results
in thread Parsing SOAP::Lite results

I see what's causing my issue with the 'Not an ARRAY ref' error. When there is only one location it's not an array.

$VAR1 = { 'Toys' => bless( { 'Toy' => [ bless( { 'ToyLocations' => bless( { 'ToyLocation' => { 'toyQuantity' => '2', 'locationName' => 'toy +box' } }, 'ArrayOfToyLocation' ), 'color' => 'brown', 'toyName' => 'bear', 'size' => 'large' }, 'Stuffed' ), bless( { 'ToyLocations' => bless( { 'ToyLocation' => [ { 'toyQuantity' => '1', 'locationName' => 'toy +box' }, { 'toyQuantity' => '4', 'locationName' => 'she +lf' } ] }, 'ArrayOfToyLocation' ), 'color' => 'none', 'toyName' => 'Sorry', 'size' => 'medium' }, 'Board' ) ] }, 'ArrayOfToy' ) };

Still stuck on what to do in this case. Would this be where introspection comes in?  pseudo:(if HASH, then ... %$location) elsif ARRAY, then ... @$location)  ? Thanks to all who are helping me learn this!

Replies are listed 'Best First'.
Re^5: Parsing SOAP::Lite results
by kcott (Archbishop) on Sep 28, 2012 at 13:21 UTC

    You can use ref to do that.

    Here's a modification of my earlier script to show this. For illustration purposes, I've made minor changes to the locationName values so you can see which toybox is being referred to.

    #!/usr/bin/env perl use strict; use warnings; my $getToysResults = { 'Toys' => bless( { 'Toy' => [ bless( { 'ToyLocations' => bless( { 'ToyLocation' => { 'toyQuantity' => '2', 'locationName' => 'HASH:toybox +' } }, 'ArrayOfToyLocation' ), 'color' => 'brown', 'toyName' => 'bear', 'size' => 'large' }, 'Stuffed' ), bless( { 'ToyLocations' => bless( { 'ToyLocation' => [ { 'toyQuantity' => '1', 'locationName' => 'ARRAY:toybo +x' }, { 'toyQuantity' => '4', 'locationName' => 'ARRAY:shelf +' } ] }, 'ArrayOfToyLocation' ), 'color' => 'none', 'toyName' => 'Sorry', 'size' => 'medium' }, 'Board' ) ] }, 'ArrayOfToy' ) }; foreach my $e (@{$getToysResults->{Toys}{Toy}}) { my $location_ref = $e->{ToyLocations}{ToyLocation}; my $location_array_ref = ref($location_ref) eq 'ARRAY' ? $location_ref : [ $location_ +ref ]; for my $location_array_hash_ref (@$location_array_ref) { print $location_array_hash_ref->{locationName}, "\n"; } }

    Output:

    $ pm_parse_soap_ref.pl HASH:toybox ARRAY:toybox ARRAY:shelf

    -- Ken

      That worked great. I had tried some  if (ref($location_ref) eq 'ARRAY') {...} elsif (ref($location_ref) eq 'HASH') {...} and just changed the @$ to a %$ and was getting a 'Can't use string ... strict refs' error. Glad you returned with the better way to do it.

      Can you tell me what's happening here?

      my $location_array_ref = ref($location_ref) eq 'ARRAY' ? $location_ref : [ $location_ +ref ];

      Thank you very much!

        That's the Conditional Operator. You'll often see it referred to as the ternary operator (actually, that's what I usually call it). That single statement could be written as:

        my $location_array_ref; if (ref($location_ref) eq 'ARRAY') { $location_array_ref = $location_ref; } else { $location_array_ref = [ $location_ref ]; }

        There's an assumption here that if it's not an arrayref, it will be a hashref. That seemed like a reasonable assumption to me; especially for a short script intended to demonstrate a technique. Furthermore, trying to guess what other data types you might be working with would simply be other assumptions.

        The basic syntax for this operator is:

        expression1 ? expression2 : expression3

        There's nothing to say how complex any of those expressions might be; in fact, any of them could include another ternary operator.

        Putting all that together, here's an example of far more robust code that allows arrayrefs, hashrefs, scalarrefs and non-reference scalar values with anything else generating an error.

        foreach my $e (@{$getToysResults->{Toys}{Toy}}) { my $location_ref = $e->{ToyLocations}{ToyLocation}; my $ref_type = ref $location_ref; my $location_array_ref = $ref_type eq 'ARRAY' ? $location_ref : $ref_type eq 'HASH' ? [ $location_ref ] : $ref_type eq 'SCALAR' ? [ { locationName => $$location_ref } + ] : $ref_type eq '' ? [ { locationName => $location_ref } +] : undef; die 'Data corrupt' unless defined $location_array_ref; for my $location_array_hash_ref (@$location_array_ref) { print $location_array_hash_ref->{locationName}, "\n"; } }

        If you want to play around with this, here's the code I used for my tests and the output. Note that I changed die to warn to see all conditions being exercised in a single run.

        -- Ken

Re^5: Parsing SOAP::Lite results
by Anonymous Monk on Sep 28, 2012 at 12:57 UTC

    Yes, ref is ref