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

I'm using the following code:

use XML::TreeBuilder; foreach my $drug ($tree->find_by_tag_name('drug')){ my @targets = $drug->look_down ('_tag', 'targets')->look_down ('_tag', + 'target')->attr_get_i('partner'); foreach my $id (@targets){print "\t$id";} my @actions = $drug->look_down ('_tag', 'targets')->look_down ('_tag', + 'known-action')->as_text; foreach my $act (@actions){print "\t$act";} }

to read through the following XML:

<drug type="small molecule"> <name>Goserelin</name> <targets> <target partner="1"> <known-action>yes</known-action> <target partner="2"> <known-action>yes</known-action> <target partner="3"> <known-action>yes</known-action> </target>

but it will only prints the first result. How can I get it to capture all of the attributes and actions and print them out for each drug?

Replies are listed 'Best First'.
Re: XML::TreeBuilder capture multiple attributes to array
by Jenda (Abbot) on Jul 06, 2012 at 01:20 UTC

    If you do not insist on using XML::TreeBuilder:

    use strict; use XML::Rules; use Data::Dumper; my $parser = XML::Rules->new( stripspaces => 7, rules => { 'known-action,name' => 'content', 'target' => sub { return $_[1]->{partner} => $_[1]->{'known-action'}; }, 'targets' => 'no content', 'drug' => 'pass no content' } ); my $data = $parser->parse(\*DATA); #print Dumper($data); print "Name: $data->{name}\nType: $data->{type}\n"; while (my ($partner, $action) = each %{$data->{targets}}) { print " $partner: $action\n"; } __DATA__ <drug type="small molecule"> <name>Goserelin</name> <targets> <target partner="1"> <known-action>yes</known-action> </target> <target partner="2"> <known-action>yes</known-action> </target> <target partner="3"> <known-action>yes</known-action> </target> </targets> </drug>

    Or if you insist on having a separate array of partners and actions:

    use strict; use XML::Rules; use Data::Dumper; my $parser = XML::Rules->new( stripspaces => 7, rules => { 'known-action,name' => 'content', 'target' => sub { return '@partners' => $_[1]->{partner}, '@actions' => $_[1 +]->{'known-action'}; }, 'targets' => 'pass no content', 'drug' => 'pass no content' } ); my $data = $parser->parse(\*DATA); #print Dumper($data); print "Name: $data->{name}\nType: $data->{type}\n"; print "Partners: ", join(', ', @{$data->{partners}}), "\n"; print "Actions: ", join(', ', @{$data->{actions}}), "\n"; __DATA__ <drug type="small molecule"> <name>Goserelin</name> <targets> <target partner="1"> <known-action>yes</known-action> </target> <target partner="2"> <known-action>yes</known-action> </target> <target partner="3"> <known-action>yes</known-action> </target> </targets> </drug>

    If the actual XML contains several <drug> tags, you should change its rule to "as array" or "by name" (uncomment the print Dumper($data); line to see what data structure you get in each case) or change it to a subroutine, that'll find the data in $_[1] in place of $data and may do whatever's necessary with that twig and forget the data that are no longer needed.

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

      Brilliant, thank you very much Jenda, that makes sense :)

Re: XML::TreeBuilder capture multiple attributes to array
by Anonymous Monk on Jul 05, 2012 at 13:25 UTC

    Its simple, retrieve branches (subtrees, objects ) instead of attributes

    Then , iterate over the list of objects and retrieve attributes

    I would stick with Re^3: XML::Twig help

      The issue is I asked a colleague for advise whilst waiting for a reply from that thread and as a result my most complete script is all written to use TreeBuilder now. I would have the same problem if I used Twig I feel. I just need it to return 1, 2, 3 etc. After writing 5 scripts, doing some serious googling and pestering colleagues via email I still can't get it to work. Google results show examples where people have used the above example or:

      @array = $drug->find_by_tag_name('groups')->find_by_tag_name('group')->as_text;

      But I can't get this to capture all the information I need. Getting very fed up and disheartened so whoever helps me out should know in advance they're doing their super good deed of the day!

        You could do your "super good deed of the day" by posting:
        • Legal XML code: you have no closing drug tag and mismatched target/targets tags.
        • Perl code which shows how you construct your TreeBuilder object and call the parse method.
        By posting an incomplete question you are likely to get incomplete answers.