Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

XML parsing Help..

by bioswami (Novice)
on Nov 10, 2006 at 08:51 UTC ( [id://583310]=perlquestion: print w/replies, xml ) Need Help??

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

Dear All, I am using XML::Simple to parse some simple xml files. However I ran into a problem with this particular example. Here is what the data dumper gives me:
'reaction' => [ { 'substrate' => { 'name' => 'cpd:C00084' }, 'name' => 'rn:R00710', 'product' => { 'name' => 'cpd:C00033' }, 'type' => 'reversible' }, { 'substrate' => [ { 'name' => 'cpd:C00068' }, { 'name' => 'cpd:C00022' } ], 'name' => 'rn:R00014', 'product' => { 'name' => 'cpd:C05125' }, 'type' => 'irreversible' },
What I want to do is get to each product and substrate name for a given reaction and print it out. Here is what I would like to do:
$data = $xml->XMLin($input_file); foreach $g (@{$data->{reaction}}) { foreach $h ($g->{substrate}){ $temp1 = $h->{name}; foreach $i ($g->{product}->{name}){ $temp2 = $i->{name} print "$temp1 -- $temp2\n"; } ] }
It works for the first product and substrate, but for the second one, since there are now TWO substrates, it doesn't seem to pick it up. The fact that this doesn't work tells me that I am wrong somewhere, but I can't for the life of me figure it out. The error I get is: Bad index while coercing array into hash Any help will be much appreciated!! Thanks in advance. Cheers, Bioswami

Replies are listed 'Best First'.
Re: XML parsing Help..
by davorg (Chancellor) on Nov 10, 2006 at 08:56 UTC

    You need to look at the "ForceArray" option in the XML::Simple documentation.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

      Thanks davorg. That does help. For someone else who comes here looking for an answer to this same Q, here is what I did:
      $xml = new XML::Simple (KeyAttr=>[],ForceArray=>['substrate','product' +]);
      So I specifically made all "substrate" and "product" elements into arrays. I then had to figure out the number of elements in that array. And this I did by:
      foreach $g (@{$data->{reaction}}){ $length = scalar @{$g->{substrate}}; print " $g->{name}, len = $length\n"; }
      Thanks for all your help! Cheers, Bioswami
Re: XML parsing Help..
by Hofmator (Curate) on Nov 10, 2006 at 09:48 UTC
    The problem seems to be solved already ... but I liked playing around with it a bit. To ease this for fellow monks who might feel likewise - here is my code, showing similar behaviour to what bioswami wanted to achieve originally.
    use strict; use warnings; use XML::Simple; use Data::Dumper; local $/; # slurp my $data = XMLin(<DATA>, KeyAttr => [], ForceArray => [qw/substrate pr +oduct/]); for my $reaction (@{$data->{reaction}}) { print $reaction->{name}, ' (', $reaction->{type}, "):\n"; for my $substrate (@{$reaction->{substrate}}) { foreach my $product (@{$reaction->{product}}) { print "\t", $substrate->{name}, " -- ", $product->{name}, +"\n"; } } } __DATA__ <root> <reaction name="rn:R00710" type="reversible"> <substrate name="cpd:C00084"/> <product name="cpd:C00033"/> </reaction> <reaction name="rn:R00014" type="irreversible"> <substrate name="cpd:C00068"/> <substrate name="cpd:C00022"/> <product name="cpd:C05125"/> </reaction> </root>

    -- Hofmator

    Code written by Hofmator 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.

      Just in case anyone is interested, here are two implementations using XML::Rules. The first one prints the reactions as it parses through the XML, the other builds a more simplified data structure.

      use strict; use warnings; use XML::Rules; use Data::Dumper; my $parser = XML::Rules->new( rules => [ substrate => sub {return '@substrates' => $_[1]->{name}}, product => sub {return '@products' => $_[1]->{name}}, reaction => sub { print "$_[1]->{name} ($_[1]->{type}):\n"; print "\tSubstrates: ", join(", ", @{$_[1]->{substrates}}) +, "\n"; print "\tProducts: ", join(", ", @{$_[1]->{products}}), +"\n\n"; return; }, ], ); $parser->parse(\*DATA); __DATA__ <root> <reaction name="rn:R00710" type="reversible"> <substrate name="cpd:C00084"/> <product name="cpd:C00033"/> </reaction> <reaction name="rn:R00014" type="irreversible"> <substrate name="cpd:C00068"/> <substrate name="cpd:C00022"/> <product name="cpd:C05125"/> </reaction> </root>
      and
      use strict; use warnings; use XML::Rules; use Data::Dumper; my $parser = XML::Rules->new( rules => [ substrate => sub {return '@substrates' => $_[1]->{name}}, product => sub {return '@products' => $_[1]->{name}}, reaction => sub { my $name = delete($_[1]->{name}); delete($_[1]->{_content}); return $name => $_[1]; }, root => 'pass no content', ], ); my $result = $parser->parse(\*DATA); print Dumper($result); while (my ($reaction, $data) = each(%$result)) { print "$reaction ($data->{type})\n"; print "\tSubstrates: ", join(", ", @{$data->{substrates}}), "\n"; print "\tProducts: ", join(", ", @{$data->{products}}), "\n\n"; } __DATA__ <root> <reaction name="rn:R00710" type="reversible"> <substrate name="cpd:C00084"/> <product name="cpd:C00033"/> </reaction> <reaction name="rn:R00014" type="irreversible"> <substrate name="cpd:C00068"/> <substrate name="cpd:C00022"/> <product name="cpd:C05125"/> </reaction> </root>

      The substrate and product rules could be rewriten like this:

      'substrate,product' => sub {return '@'.$_[0].'s' => $_[1]->{name}},

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (6)
As of 2024-04-16 07:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found