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

I'm trying to print out only the "sampling" of a given language. For example I want to print out only 16000 and 11025 if country = SouthAfrica and language = English. This is the code i have
#!/usr/bin/perl #use strict; use XML::Simple; use Data::Dumper; my $filename = 'C:/perlfiles/request1.xml'; my $library = XMLin($filename, ForceArray => 1 ); #print Dumper ($library) ; my $count=0; foreach my $key ( keys %{$library->{place}} ) { if ($key eq 'SouthAfrica') { foreach my $loopback ( @{$library->{place}{$key}{dataName}} ) { if ($loopback eq 'English') { print "key = $key; loopback = $loopback\n" ; } } } }
and my XML file
<?xml version="1.0" encoding="utf-8" ?> <request> <place> <country>SouthAfrica</country> <sports>cricket</sports> <sports>rugby</sports> <dataName> <language>English</language> <sampling>16000</sampling> <sampling>11025</sampling> </dataName> <dataName> <language>Africans</language> <sampling>16000</sampling> </dataName> </place> </request>

Replies are listed 'Best First'.
Re: Problem traversing nested nodes XML::Simple
by mscharrer (Hermit) on Apr 29, 2008 at 15:05 UTC
    Next time be a little more specific what your problem is. This looks like "I'm not able to go any further, please do my work for me" to me.

    When you enable:

    use strict; use warnings;
    as you always should and have a look to the Data::Dumper output you see that $library->{place} returns an array of hashrefs not a single hashref. This is you problem. You can start with changing
    foreach my $key ( keys %{$library->{place}} ) {
    to
    foreach my $place ( @{$library->{place}} ) { foreach my $country ( @{$place->{country}}) { if ($country eq 'SouthAfrica') {
Re: Problem traversing nested nodes XML::Simple
by toolic (Bishop) on Apr 29, 2008 at 15:21 UTC
    Elaborating a bit on mscharrer's point, I also added use diagnostics; and ran your script. This generated the following warning:
    Pseudo-hashes are deprecated at ... (D deprecated) Pseudo-hashes were deprecated in Perl 5.8.0 and th +ey will be removed in Perl 5.10.0, see perl58delta for more details. You can continue to use the fields pragma.

    which pointed to your line:

    foreach my $key ( keys %{$library->{place}} )

    I understand that this is not the root of your problem, but it does underscore the usefulness of the warnings/strict/diagnostics pragmas in helping you to debug your code. Of course, if you are using an older version of perl, you probably would not have seen this message.

Re: Problem traversing nested nodes XML::Simple
by dragonchild (Archbishop) on Apr 29, 2008 at 15:04 UTC
    Your problem is that you're trying to use a data structure for a purpose it wasn't designed to handle. Transform the structure first, then manipulate it. Much saner.

    Plus, it's Afrikaans, not Africans.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Problem traversing nested nodes XML::Simple
by Jenda (Abbot) on Apr 30, 2008 at 19:09 UTC
    You can either print the samplings as you parse the XML:
    use strict; use XML::Rules; my $parser = XML::Rules->new( rules => { _default => 'content', 'sampling,sports' => 'content array', dataName => sub { return unless $_[1]->{language} eq 'English'; return $_[0] => $_[1]; }, place => sub { return unless $_[1]->{country} eq 'SouthAfrica'; print "Sampling: " . join( ', ', @{$_[1]->{dataName}{sampl +ing}}) . "\n"; return; } } ); $parser->parse(\*DATA); __DATA__ <?xml version="1.0" encoding="utf-8" ?> <request> <place> <country>SouthAfrica</country> <sports>cricket</sports> <sports>rugby</sports> <dataName> <language>English</language> <sampling>16000</sampling> <sampling>11025</sampling> </dataName> <dataName> <language>Africans</language> <sampling>16000</sampling> </dataName> </place> </request>
    or extract the data you need in an easy to use format and print what you need then:
    use strict; use XML::Rules; my $parser = XML::Rules->new( rules => { _default => 'content', 'sampling,sports' => 'content array', dataName => sub { return delete($_[1]->{language}) => $_[1]; }, place => sub { return delete($_[1]->{country}) => $_[1]; }, request => 'pass', }, stripspaces => 7 ); my $data = $parser->parse(\*DATA); #use Data::Dumper; #print Dumper($data); print "Sampling: " . join( ', ', @{$data->{SouthAfrica}{English}{sampl +ing}}) . "\n"; __DATA__ <?xml version="1.0" encoding="utf-8" ?> ...