Roboz has asked for the wisdom of the Perl Monks concerning the following question:

I've been having some good success coding a SOAP::Lite client to retrieve and parse a complicated XML document from an Apache web service, especially due to great help I've found reading this web site. Now I'm stuck on a problem and need to finally ask a question of the Perl Monks:

The following code, XML data and Dumper output has been simplified to concentrate on the issue at hand. The data doesn't make much sense but I have maintained the complexity of what I'm working with. I have no control over the XML schema.

OK, when I run this code it usually does what I need it to do, which is retrieve an XML document, parse the document for specific data and output it as text.

#!/usr/bin/perl # use SOAP::Lite; use Data::Dumper; # Load server CA certificate # $ENV{HTTPS_CA_FILE} = "ca.cer"; my $soap = SOAP::Lite->new(proxy => 'https://webserver:443/ToyService/ +ToyWebService'); $soap->readable(1); # Setup credentials # sub SOAP::Transport::HTTP::Client::get_basic_credentials { return 'user' => 'pass'; # Execute SOAP Request # sub doSOAPCall { my $method = $_[0]; eval { my $res = $soap->call($method)->result; return $res; 1; } or die; } # Build method and parameters for GetToys and execute call to web serv +ice # my $getFrobMethod = SOAP::Data->name('getToys') ->attr({xmlns => 'web.service.namespace'}); my $getToysResults = &doSOAPCall($getToysMethod); # Print output for TOYS # foreach my $e (@{$getToysResults->{Toys}{Toy}}){ print "$e->{color}\n"; if ($e->{ToyLocations}{ToyLocation}{locationName}){ $location = $e->{ToyLocations}{ToyLocation}{locationName}; $location = "XXX$location"; } else { $location = "XXX"; } print "TOYS\/$e->{color}\/$e->{ToyLocations}{ToyLocation}{toyName} +\/$e->{ToyLocations}{ToyLocation}{toyQuantity}\/$location\/\n"; }

The problem hits when there is an additional array of data within the XML. This gives the following error:
Not a HASH reference at ... line ...
This would specifically be at the if statement when there's more than one "ToyLocation".

Here's the resulting XML data from SOAP::Lite debug:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xm +lns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenc="ht +tp://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org +/2001/XMLSchema"> <env:Header/> <env:Body> <m:getResponse xmlns:m="web.service.namespace"> <ns1:result xmlns:n1="web.service.namespace"> <ns2:Toys xmlns:ns2="web.service.namespace.value" xsi: +type="ns2:ArrayOfToy"> <ns2:Toy xsi:type="ns2:Stuffed"> <ns2:ToyLocations xsi:type="ns2:ArrayOfToyLoca +tion"> <ns2:ToyLocation> <ns2:toyName>bear</ns2:toyName> <ns2:toyQuantity>2</ns2:toyQuantity> <ns2:locationName>toybox</ns2:location +Name> </ns2:ToyLocation> </ns2:ToyLocations> <ns2:color>brown</ns2:color> <ns2:size>large</ns2:size> </ns2:Toy> <ns2:Toy xsi:type="ns2:Electric"> <ns2:ToyLocations xsi:type="ns2:ArrayOfToyLoca +tion"> <ns2:ToyLocation> <ns2:toyName>car</ns2:toyName> <ns2:toyQuantity>2</ns2:toyQuantity> <ns2:locationName>shelf</ns2:locationN +ame> </ns2:ToyLocation> </ns2:ToyLocations> <ns2:color>red</ns2:color> <ns2:size>small</ns2:size> </ns2:Toy> <ns2:Toy xsi:type="ns2:Board"> <ns2:ToyLocations xsi:type="ns2:ArrayOfToyLoca +tion"> <ns2:ToyLocation> <ns2:toyName>Sorry</ns2:toyName> <ns2:toyQuantity>1</ns2:toyQuantity> <ns2:locationName>toybox</ns2:location +Name> </ns2:ToyLocation> <ns2:ToyLocation> <ns2:toyName>Chess</ns2:toyName> <ns2:toyQuantity>4</ns2:toyQuantity> <ns2:locationName>shelf</ns2:locationN +ame> </ns2:ToyLocation> </ns2:ToyLocations> <ns2:color>none</ns2:color> <ns2:size>medium</ns2:size> </ns2:Toy> </ns2:Toys> </ns1:result> </m:getResponse> </env:Body> </env:Envelope>

and here's the Dumper results:

$VAR1 = { 'Toys' => bless( { 'Toy' => [ bless( { 'ToyLoc +ations' => bless( { + 'ToyLocation' => { + 'toyName' => 'bear', + 'toyQuantity' => '2', + 'locationName' => 'toybo +x' + } + }, 'ArrayOfToyLocation' ), 'color' + => 'brown', 'size' +=> 'large' }, 'Stuff +ed' ), bless( { 'ToyLoc +ations' => bless( { + 'ToyLocation' => { + 'toyName' => 'car', + 'toyQuantity' => '2', + 'locationName' => 'shelf +' + } + }, 'ArrayOfToyLocation' ), 'color' + => 'red', 'size' +=> 'small' }, 'Elect +ric' ), bless( { 'ToyLoc +ations' => bless( { + 'ToyLocation' => [ + { + 'toyName' => ' +Sorry', + 'toyQuantity' +=> '1', + 'locationName' + => 'toybox' + }, + { + 'toyName' => ' +Chess', + 'toyQuantity' +=> '4', + 'locationName' + => 'shelf' + } + ] + }, 'ArrayOfToyLocation' ), 'color' + => 'none', 'size' +=> 'medium' }, 'Board +' ) ] }, 'ArrayOfToy' ) };

I've tried a nested foreach to go through the locations like so:

foreach my $e (@{$getToysResults->{Toys}{Toy}}){ foreach my $f (@{$e->{ToyLocations}{ToyLocation}}){ ...

and get a "Can't use undefined value as a HASH reference at..." error. I've also tried using 'valueof' and 'dataof' and XPath as noted in the SOAP::SOM docs and get a "Can't call method "dataof" on unblessed reference at ..." error.

Any help is appreciated!

Replies are listed 'Best First'.
Re: Problem parsing SOAP::SOM result of SOAP::Lite client Query - Not a HASH reference
by McA (Priest) on Sep 27, 2012 at 12:27 UTC
    Hi

    I didn't look at the whole problem, sorry. But if you want to react in your code to changing structures at runtime you can use the possiblilies of introspection:

    if(ref $somthing && ref $something eq 'HASH') { # iterate over keys } elsif (ref $somthing && ref $something eq 'ARRAY') { # iterate over list } else { # do whatever you want }

    I hope it's an idea to help yourself

    Best regards
    McA

      Thanks for the input. I didn't realize you could do that. I know that the data structure will be the same with regards to ARRAY vs. HASH. I pretty sure the problem is due to my code trying to access the same data that's in two (or more) locations (which I'll have to access each of them to create a new line of text output.) I'm pretty sure I'll need to use nested foreach statements. I'm just having trouble figuring out how to do this with this wierd data structure that has hashes and arrays and bless statements...