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

I'm trying to handle the data coming from the Adwords API with XML::Simple, which seems to be a decent choice for it, but I can't seem to grab the values that I need, which are just so deep into the XML. I'm not sure where I'm wrong, but maybe you can help:
#!/usr/bin/perl -w use strict; use XML::Simple; use Data::Dumper; my $content = XMLin(qq| <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envel +ope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http:// +www.w3.org/2001/XMLSchema-instance"> <soapenv:Header> <responseTime soapenv:actor="http://schemas.xmlsoap.org/soap/actor/n +ext" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api +/adwords/v5">124</responseTime> <operations soapenv:actor="http://schemas.xmlsoap.org/soap/actor/nex +t" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/a +dwords/v5">1</operations> <units soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" so +apenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adword +s/v5">25</units> <requestId soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next +" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/ad +words/v5">123</requestId> </soapenv:Header> <soapenv:Body> <getKeywordVariationsResponse xmlns="https://adwords.google.com/api/ +adwords/v5"> <getKeywordVariationsReturn> <moreSpecific> <text>web content managment</text> <language>en</language> <advertiserCompetitionScale>3</advertiserCompetitionScale> <searchVolumeScale>1</searchVolumeScale> </moreSpecific> <moreSpecific> <text>web based content management</text> <language>en</language> <advertiserCompetitionScale>5</advertiserCompetitionScale> <searchVolumeScale>1</searchVolumeScale> </moreSpecific> <additionalToConsider> <text>3.14</text> <language>en</language> <advertiserCompetitionScale>3</advertiserCompetitionScale> <searchVolumeScale>2</searchVolumeScale> </additionalToConsider> <additionalToConsider> <text>babinet's</text> <language>en</language> <advertiserCompetitionScale>2</advertiserCompetitionScale> <searchVolumeScale>0</searchVolumeScale> </additionalToConsider> </getKeywordVariationsReturn> </getKeywordVariationsResponse> </soapenv:Body> </soapenv:Envelope> |); print "... $content->{'soapenv:Envelope'}{'soapenv:Body'}{'getKeywordV +ariationsResponse'}{'getKeywordVariationsReturn'}{'moreSpecific'}[0]- +>{'text'} ...\n"; #print Dumper(\$content);
This returns me nothing, and I even get the fun uninitialized statement:
Use of uninitialized value in concatenation (.) or string at xmlparser +.pl line 54.
Any ideas? Obviously I need what's in the text, advertiserCompetitionScale, and searchVolume tags, and I need to iterate through each occurrence. Any ideas?


Michael

Replies are listed 'Best First'.
Re: Trouble Getting Deep Into a Hash from XML::Parser
by runrig (Abbot) on Sep 02, 2006 at 07:12 UTC
    By default the root tag is dropped and not included in the hash. Use the KeepRoot option if you want to include it. Otherwise just use:
    print $content->{'soapenv:Body'}{getKeywordVariationsResponse}{getKeyw +ordVariationsReturn}{moreSpecific}[0]{text},"\n";
    (also note that your title is misleading as this is an XML::Simple question, not really XML::Parser).
      One more associated problem, how do I know how many to expect when I am running through adding each of these to an array of hashes? There's got to be a better way to handle it. The code below works, but the XML has a different number of results each time for the "moreSpecific" tag. Any suggestions??
      my $moreListCnt = 2; my $cnt = 0; my @morelist; for (;$cnt<$moreListCnt;$cnt++) { $morelist[$cnt]{'more'} = $content->{'soapenv:Body'}{'getKeywordVa +riationsResponse'}{'getKeywordVariationsReturn'}{'moreSpecific'}[$cnt +]->{'text'}; $morelist[$cnt]{'comp'} = $content->{'soapenv:Body'}{'getKeywordVa +riationsResponse'}{'getKeywordVariationsReturn'}{'moreSpecific'}[$cnt +]->{'advertiserCompetitionScale'}; $morelist[$cnt]{'pop'} = $content->{'soapenv:Body'}{'getKeywordVar +iationsResponse'}{'getKeywordVariationsReturn'}{'moreSpecific'}[$cnt] +->{'searchVolumeScale'}; }
      Thanks!!


      Michael
        First, I would use ForceArray on the moreSpecific tag (if there is only one 'moreSpecific' node, it will not make an array), and you don't need the count to access all of the subnodes.
        my $morelist = $content->{'soapenv:Body'}{'getKeywordVariationsRespons +e'}{'getKeywordVariationsReturn'}{'moreSpecific'}; for my $more (@$morelist) { print "$more->{text}\n"; print "$more->{advertiserCompetitionScale}\n"; print "$more->{searchVolumeScale}\n"; $more->{more} = $more->{text}; $more->{comp} = $more->{advertiserCompetitionScale}; $more->{pop} = $more->{searchVolumeScale}; }
        Update: fixed, updated example.
      Works like a charm! Thanks for pointing that out, I think I was just looking at some XML::Parser documentation...


      Michael