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?
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"
}
}
| [reply] [d/l] [select] |
|
| [reply] |
|
| [reply] |
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
| [reply] [d/l] [select] |
|
| [reply] |
Re: Parsing SOAP::Lite results
by kcott (Archbishop) 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";
}
to
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).]
| [reply] [d/l] [select] |
|
Tried your suggestion and received a 'Not an ARRAY reference' error on the for my $location_array_hash_ref (@$location_array_ref) { line...
Yeah, the naming convention is due to an XML schema I have no control over.
| [reply] [d/l] |
|
#!/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:
$ pm_parse_soap_hash.pl
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.
| [reply] [d/l] [select] |
|
|
|
|
|
Hi,
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
| [reply] [d/l] |
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
| [reply] |
|
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.
| [reply] |
|
|