Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Parsing SOAP::Lite results

by Roboz (Novice)
on Sep 28, 2012 at 09:48 UTC ( #996156=perlquestion: print w/replies, xml ) Need Help??
Roboz has asked for the wisdom of the Perl Monks concerning the following question:

OK, I'm re-asking this question in a more succinct, focused manner (I hope.) I'm trying to parse a SOAP::Lite result data structure. Here's an example:

$VAR1 = { 'Toys' => bless( { 'Toy' => [ bless( { 'ToyLocations' => bless( { 'ToyLocation' => [ { 'toyQuantity' => '1', 'locationName' => 'toybox' }, { 'toyQuantity' => '4', 'locationName' => 'shelf' } ] }, 'ArrayOfToyLocation' ), 'color' => 'none', 'toyName' => 'Sorry', 'size' => 'medium' }, 'Board' ) ] }, 'ArrayOfToy' ) };

I'd like to list the locations and quantities along with the name from the parent node. When I parsed the data with only a single location the following code worked:

foreach my $e (@{$getToysResults->{Toys}{Toy}}){ print "$e->{ToyLocations}{ToyLocation}{locationName}\n"; }

But it errored with "Not a HASH reference" when there was more than one location. That's when I realized I had to use nested foreach statements to do what I needed. BUT I can't figure out how to get at the list of locations! I've tried the following:

foreach my $e (@{$getToysResults->{Toys}{Toy}{ToyLocations}{ToyLocatio +n}}){ print "$e->{locationName}\n"; }

and I get a 'Not an ARRAY reference' error. What am I doing wrong?

Replies are listed 'Best First'.
Re: Parsing SOAP::Lite results
by shmem (Chancellor) on Sep 28, 2012 at 10:18 UTC
    and I get a 'Not an ARRAY reference' error. What am I doing wrong?

    Well, I get a Not a HASH reference error, since $getToysResults->{Toys}->{Toy} is of type ARRAY:

    $VAR1 = { 'Toys' => bless( { 'Toy' => [ <----- here ... ] }, 'ArrayOfToy' ) };

    You need two loops.

    for my $toy ( @{$getToysResults->{Toys}->{Toy}} ) { for my $l ( @{$toy->{ToyLocations}->{ToyLocation}} ) { print "$l->{locationName}\n" } }

      I got the 'Not an ARRAY reference' error when I tried the second loop, as in your suggestion. That was the part that was confusing me.

        shmem's code should not have produced that error with the data you posted. My code (Re: Parsing SOAP::Lite results) should not have produced the same error either. I'm now reasonably certain that the data you've posted is not the data you're operating on. See my earlier response (Re^3: Parsing SOAP::Lite results) for some suggestions.

        -- Ken

Re: Parsing SOAP::Lite results
by Anonymous Monk on Sep 28, 2012 at 10:32 UTC

    What am I doing wrong?

    Basically, $getToysResults->{Toys}{Toy} is an arrayref and not a hash

    #!/usr/bin/perl -- use strict; use warnings; my $getToysResults = { 'Toys' => bless( { 'Toy' => [ bless( { 'ToyLocations' => bless( { 'ToyLocation' => [ { 'toyQuantity' => '1', 'locationName' => 'toybox' }, { 'toyQuantity' => '4', 'locationName' => 'shelf' } ] }, 'ArrayOfToyLocation' ), 'color' => 'none', 'toyName' => 'Sorry', 'size' => 'medium' }, 'Board' ) ] }, 'ArrayOfToy' ) }; warn $getToysResults; warn $getToysResults->{Toys}; warn $getToysResults->{Toys}{Toy}; warn $getToysResults->{Toys}{Toy}{ToyLocations}; __END__ HASH(0x9b4fdc) at - line 27. ArrayOfToy=HASH(0x9b506c) at - line 28. ARRAY(0x9b50bc) at - line 29. Not a HASH reference at - line 30.

    See diagnostics, References quick reference, ref, Data::Diver, SOAP::SOM#valueof(node)

    use Data::Diver qw/ Dive /; my $toyArrRef = Dive $getToysResults, qw/ Toys Toy /; for my $toy ( @{ $toyArrRef } ){ my $arrToyLoc = Dive $toy, qw/ ToyLocations ToyLocation /; for my $hash ( @{ $arrToyLoc } ){ print "$$hash{locationName}\n"; } } __END__ toybox shelf

      The Data::Diver module looks interesting. Alas, I'm coding on an offline system so I'll have wait a bit before trying that. Thank you for the insight though.

Re: Parsing SOAP::Lite results
by kcott (Chancellor) on Sep 28, 2012 at 10:38 UTC

    G'day Roboz,

    You effectively have arrays nested within arrays, so you'll need some sort of nested loops to read the data.

    If you change

    foreach my $e (@{$getToysResults->{Toys}{Toy}}){ print "$e->{ToyLocations}{ToyLocation}{locationName}\n"; }


    foreach my $e (@{$getToysResults->{Toys}{Toy}}) { my $location_array_ref = $e->{ToyLocations}{ToyLocation}; for my $location_array_hash_ref (@$location_array_ref) { print $location_array_hash_ref->{locationName}, "\n"; } }

    you'll get the results you're after.

    [Aside: On first viewing, I found your naming conventions somewhat counter-intuitive: Toys and ToyLocations are both plural but point to single objects; Toy and ToyLocation are both singular but point to (potentially) multiple objects. In the context of the Dumper output, it's fairly easy to see what's intended; in isolation, those names may easily lead to problems of misinterpretation (particularly if you or someone else revisits the code 12 months down the track).]

    -- Ken

      Tried your suggestion and received a 'Not an ARRAY reference' error on the for my $location_array_hash_ref (@$location_array_ref) {

      Yeah, the naming convention is due to an XML schema I have no control over.

        I tested that code before posting. I changed $VAR1 to my $getToysResults but otherwise left the Dumper output unchanged. Here's the script ( in its entirety:

        #!/usr/bin/env perl use strict; use warnings; my $getToysResults = { 'Toys' => bless( { 'Toy' => [ bless( { 'ToyLocations' => bless( { 'ToyLocation' => [ { 'toyQuantity' => '1', 'locationName' => 'toybox' }, { 'toyQuantity' => '4', 'locationName' => 'shelf' } ] }, 'ArrayOfToyLocation' ), 'color' => 'none', 'toyName' => 'Sorry', 'size' => 'medium' }, 'Board' ) ] }, 'ArrayOfToy' ) }; foreach my $e (@{$getToysResults->{Toys}{Toy}}) { my $location_array_ref = $e->{ToyLocations}{ToyLocation}; for my $location_array_hash_ref (@$location_array_ref) { print $location_array_hash_ref->{locationName}, "\n"; } }

        Here's the output:

        $ toybox shelf

        Try running that script. If it works, then maybe the data you posted was incorrect (typo, cut-and-paste error, etc.) or perhaps you used my code differently. Try adding print Dumper $getToysResults; just before the outer foreach in your code and see if it produces the same output as you originally posted.

        If it doesn't work, please post full output along with your Perl version and OS: as it stands, I can't see anything there that wouldn't work on any version of Perl 5.

        -- Ken


        Kcott's code works perfectly.
        Maybe, you are not saving the changes you made to your script.
        However, if the double for loop used confuses you, you can consider this:

        use warnings; use strict; my $getToysResults = { 'Toys' => bless( { 'Toy' => [ bless( { 'ToyLocations' => bless( { 'ToyLocation' => [ { 'toyQuantity' => '1', 'locationName' => 'toybox' }, { 'toyQuantity' => '4', 'locationName' => 'shelf' } ] }, 'ArrayOfToyLocation' ), 'color' => 'none', 'toyName' => 'Sorry', 'size' => 'medium' }, 'Board' ) ] }, 'ArrayOfToy' ) }; foreach my $e ( @{ $getToysResults->{Toys}{Toy} } ) { print join "\n" => map { $_->{locationName} } @{ $e->{ToyLocations}{ToyLocation} }; }

        If you tell me, I'll forget.
        If you show me, I'll remember.
        if you involve me, I'll understand.
        --- Author unknown to me
Re: Parsing SOAP::Lite results (walking/navigating/iterating over)
by Anonymous Monk on Sep 28, 2012 at 10:41 UTC

    I'm trying to parse a SOAP::Lite result data structure. Here's an example:

    Usually "parsing" is building a data structure, and what you're actually trying to , we call walking or navigating or to-iterate-over a data structure

      Thanks on the correction. I had 'iterate over' and changed it to 'parsing.' So, in this case SOAP::Lite parsed the XML result into a SOAP::SOM data structure that I am walking through, navigating, etc... Definately worth knowing the proper words to convey the proper ideas.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://996156]
Approved by shmem
and the monks are mute...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (6)
As of 2017-08-20 08:35 GMT
Find Nodes?
    Voting Booth?
    Who is your favorite scientist and why?

    Results (313 votes). Check out past polls.