Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

XML::Simple: Loop through childnodes?

by Spidy (Chaplain)
on Feb 16, 2007 at 00:32 UTC ( [id://600338]=perlquestion: print w/replies, xml ) Need Help??

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

Greetings, fellow monks.

A recent project I've been working on involves XML that looks like this:

<inbox> <item> <title>Foo</title> <description>Bar</description> </item> <item> <title>Baz</title> <description>Foobar</description> </item> </inbox>

We want to load in the XML, and then loop through all of the childnodes of the inbox element(the <item>'s), before outputting a list of all the item titles. We have loaded in the XML file using this snippet:

my $xml = XML::Simple->new(); my $d = $xml->XMLin("$u.xml",ForceArray => 1, KeepRoot => 1);

Does anyone know what I would need to do to loop through each item under inbox?

Thanks,
Spidy

Replies are listed 'Best First'.
Re: XML::Simple: Loop through childnodes?
by GrandFather (Saint) on Feb 16, 2007 at 01:01 UTC

    I find generally that XML::Simple ain't. Try XML::TreeBuilder or XML::Twig instead:

    use strict; use warnings; use XML::TreeBuilder; use XML::Twig; my $xml = do {local $/; <DATA>}; print "Using TreeBuilder:\n"; my $root = XML::TreeBuilder->new (); $root->parse ($xml); my @itemNodes = $root->look_down ('_tag', 'item'); my $nodeCount; for my $nodeIndex (0 .. @itemNodes - 1) { my @titles = $itemNodes[$nodeIndex]->look_down ('_tag', 'title'); print "Item node " . ($nodeIndex + 1) . "\n"; print " ", $_->as_text (), "\n" for @titles; } print "\nUsing Twig\n"; my $t= XML::Twig->new (twig_roots => {item => \&data}); $t->parse ($xml); sub data { my ($t, $data) = @_; ++$nodeCount; print "Item node $nodeCount\n"; my @titles = $data->descendants ('title'); print " ", $_->trimmed_text (), "\n" for @titles; } __DATA__ <inbox> <item> <title>Foo</title> <description>Bar</description> </item> <item> <title>Baz</title> <description>Foobar</description> </item> </inbox>

    Prints:

    Using TreeBuilder: Item node 1 Foo Item node 2 Baz Using Twig Item node 1 Foo Item node 2 Baz

    DWIM is Perl's answer to Gödel

      I will agree with GrandFather, unless the document is very simple XML, XML::Simple takes some work to get right. Use XML::Twig, it might take a little while to figure it out but is more scalable than XML::Simple.

      I am writing my first ever Perl script, and without this example I would be lost. Thanks a lot for posting this!
Re: XML::Simple: Loop through childnodes?
by xdg (Monsignor) on Feb 16, 2007 at 02:29 UTC
    Does anyone know what I would need to do to loop through each item under inbox?

    Sure. Determine what $d looks like and then walk that data structure.

    use Data::Dump::Streamer; my $xml = XML::Simple->new(); my $d = $xml->XMLin("sample.xml",ForceArray => 1, KeepRoot => 1); Dump $d;

    Gives

    $HASH1 = { inbox => [ { item => [ { description => [ 'Bar' ], title => [ 'Foo' ] }, { description => [ 'Foobar' ], title => [ 'Baz' ] } ] } ] };

    Now, for that sample XML it seems to have lots of redundant hashes and arrays if what you care about are the items. So, maybe don't use the extra options:

    my $d = $xml->XMLin("sample.xml");

    Gives:

    $HASH1 = { item => [ { description => 'Bar', title => 'Foo' }, { description => 'Foobar', title => 'Baz' } ] };

    So with that, $d->{item} would appear to be an AOH. Loop through it like this:

    for my $item ( @{ $d->{item} } ) { print "$item->{title}\n"; # for example }

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      This is a bit problematic since if there ever was XML that'd contain only one <item> tag the structure would look different. With recent enough XML::Simple you can specify what tags to force to arrays$xml->XMLin("sample.xml",ForceArray => ['item']).

      In case the XML starts to get bigger, it may be better to start using something different. XML::Twig or ... everyone guessed by now ... XML::Rules ;-)

      use XML::Rules; my $parser = XML::Rules->new( rules => [ 'description,title' => 'content', 'item' => sub { print "$_[1]->{title}: $_[1]->{description}\n" return; }, 'inbox' => '', ], ); $parser->parse('sample.xml');

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://600338]
Approved by GrandFather
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-19 17:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found