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

Hello everyone:
<?xml version='1.0'?> <queries> <query> <name>topHosts</name> <layer>LINK</layer> <topN>20</topN> <datatype>topHosts</datatype> <filter></filter> </query> <query> <name>topHosts</name> <layer>LINK</layer> <topN>120</topN> <datatype>topHosts</datatype> <filter></filter> </query> </queries>
This is the xml file. The code which is reading it is given below.
$simple1 = XML::Simple->new(ForceArray=>1, KeepRoot=>1); $data1 = $simple->XMLin("query.xml"); print Dumper($data1) . "\n";
Result:
$VAR1 = { 'queries' => [ { 'query' => [ { 'topN' => [ '20' ], 'layer' => [ 'LINK' ], 'filter' => [ {} ], 'name' => [ 'topHosts' ], 'datatype' => [ 'topHosts' ] }, { 'topN' => [ '120' ], 'layer' => [ 'LINK' ], 'filter' => [ {} ], 'name' => [ 'topHosts' ], 'datatype' => [ 'topHosts' ] } ] } ] };
Now, I want to read the key elements (topN, layer, filter, name, datatype) values in different variables. But I did not find a any code which will read the above data structure and extract those values, looping through throught the number of queries. For example - code in this case should loop 2 times as you can see that there are two queries in the xml file (query.xml). Please help!

Replies are listed 'Best First'.
Re: XML parsing problem
by ambrus (Abbot) on Mar 10, 2009 at 20:13 UTC

    In the chatterbox the OP said he is trying to use XML::Simple as he does not understand XML::Twig. Please allow me to show an XML::Twig example in case he changes his mind.

    use warnings; use strict; use XML::Twig; my $twig = XML::Twig->new; $twig->parse(*DATA); for my $query ($twig->findnodes("/queries/query")) { my $name = $query->field("name"); my $topN = $query->field("topN"); print "query name=($name) topN=($topN)\n"; } __DATA__ <?xml version='1.0'?> <queries> <query> <name>topHosts</name> <layer>LINK</layer> <topN>20</topN> <datatype>topHosts</datatype> <filter></filter> </query> <query> <name>topHosts</name> <layer>LINK</layer> <topN>120</topN> <datatype>topHosts</datatype> <filter></filter> </query> </queries>
      And here's an XML::Rules example:
      #!/usr/bin/perl use strict; use warnings; use XML::Rules; my @rules = ( _default => sub {$_[0] => $_[1]->{_content}}, query => sub { print "$_[1]->{name}: $_[1]->{topN}\n" }, ); my $r = XML::Rules->new(rules => \@rules); $r->parse(\*DATA); __END__ <?xml version='1.0'?> <queries> <query> <name>topHosts</name> <layer>LINK</layer> <topN>20</topN> <datatype>topHosts</datatype> <filter></filter> </query> <query> <name>topHosts</name> <layer>LINK</layer> <topN>120</topN> <datatype>topHosts</datatype> <filter></filter> </query> </queries>
      Here's an alternate example:
      use strict; use warnings; use XML::Rules; use Data::Dumper qw(Dumper); my @rules = ( _default => 'content', # Default filter to 'None' (v5.10 syntax here) filter => sub { $_[1]{_content} //= 'None'; $_[0] => $_[1]{_conten +t} }, query => 'no content array', queries => 'pass no content', ); my $r = XML::Rules->new(rules => \@rules); my $data = $r->parse(\*DATA); # Assume same data as above print Dumper $data; # Output $VAR1 = { 'query' => [ { 'topN' => '20', 'layer' => 'LINK', 'filter' => 'None', 'name' => 'topHosts', 'datatype' => 'topHosts' }, { 'topN' => '120', 'layer' => 'LINK', 'filter' => 'None', 'name' => 'topHosts', 'datatype' => 'topHosts' } ] };

        And what would an XML-thread be without the third blind mouse; XML::LibXML.

        use XML::LibXML; my $parser = XML::LibXML->new(); my $doc = $parser->parse_file("query.xml"); for my $query ( $doc->findnodes("//queries/query") ) { for my $kid ( $query->findnodes("*") ) { printf("%10s : %s\n", $kid->getName, $kid->textContent ); } print "\n"; }
Re: XML parsing problem
by ikegami (Patriarch) on Mar 10, 2009 at 19:55 UTC
    The KeepRoot=>1 is only making things more complicated.
    use strict; use warnings; use Data::Dumper qw( Dumper ); use XML::Simple qw( ); my $xs = XML::Simple->new( ForceArray => 1, ); my $tree = $xs->xml_in("query.xml"); print(0+@{ $tree->{query} }, " queries\n"); for my $query (@{ $tree->{query} }) { print(Dumper($query)); }
Re: XML parsing problem
by CountZero (Bishop) on Mar 10, 2009 at 19:52 UTC
    If you want to query your XML-file, XPath is the way: And here is an example (I slightly changed your XML-file so all elements have different content):
    use strict; use XML::XPath; use XML::XPath::XMLParser; my $xp = XML::XPath->new( xml => *DATA ); print 'There are ', $xp->find('queries/query')->size, " 'query' nodes. +\n"; foreach my $element (qw/topN layer filter name datatype/) { print "$element: ", ( join ', ', map { XML::XPath::XMLParser::as_string($_) =~ />([^<]*)</ } $xp->find("queries/query/$element")->get_nodelist ), "\n"; } ## end foreach my $element (qw/topN layer filter name datatype/) __DATA__ <?xml version='1.0'?> <queries> <query> <name>topHosts 20</name> <layer>LINK 20</layer> <topN>20</topN> <datatype>topHosts 20</datatype> <filter></filter> </query> <query> <name>topHosts 120</name> <layer>LINK 120</layer> <topN>120</topN> <datatype>topHosts 120</datatype> <filter></filter> </query> </queries>
    Output:
    There are 2 'query' nodes. topN: 20, 120 layer: LINK 20, LINK 120 filter: name: topHosts 20, topHosts 120 datatype: topHosts 20, topHosts 120
    And yes I know that the regex to extract the text-data from the nodes is very crude ...

    Update: added a code example.

    Update2: simplified and shortened code

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      If you want to query your XML-file, XPath is the way

      I agree with that 100% however XML::LibXML is a much better module to recommend than XML::XPath. The libxml implementation is much faster, uses less memory and is currently being maintained.

Re: XML parsing problem
by Herkum (Parson) on Mar 10, 2009 at 19:44 UTC

    You have data all there, I don't understand what you mean "at run-time" because XML::Simple is doing everything at run-time already. Perhaps you don't know how to work with the given data structure?