Home > Archive > PERL Beginners > April 2007 > how to use xml::simple with nested for loops???
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
how to use xml::simple with nested for loops???
|
|
| Dave Gauthier 2007-04-03, 7:02 pm |
| Hi:
I'm trying to construct something like a nested for loop structuire to
read the data out of an XML file using XML::Simple.
Here's the sample xml...
<?xml version='1.0' encoding='ISO-8859-1' ?>
<ARBORINFO NAME="Info about Trees">
<FOREST NAME="olympic">
<TREE NAME="spruce">
<DIAM_HEIGHT>4.0,70.0</DIAM_HEIGHT>
<DIAM_HEIGHT>4.1,70.1</DIAM_HEIGHT>
<DIAM_HEIGHT>4.2,70.2</DIAM_HEIGHT>
</TREE>
<TREE NAME="redwood">
<DIAM_HEIGHT>5.0,80.0</DIAM_HEIGHT>
<DIAM_HEIGHT>5.1,80.1</DIAM_HEIGHT>
<DIAM_HEIGHT>5.2,80.2</DIAM_HEIGHT>
<DIAM_HEIGHT>5.3,80.3</DIAM_HEIGHT>
</TREE>
<TREE NAME="pine">
<DIAM_HEIGHT>3.0,60.0</DIAM_HEIGHT>
<DIAM_HEIGHT>3.1,60.1</DIAM_HEIGHT>
</TREE>
</FOREST>
<FOREST NAME="acadia">
<TREE NAME="birch">
<DIAM_HEIGHT>1.0,30.0</DIAM_HEIGHT>
<DIAM_HEIGHT>1.1,30.1</DIAM_HEIGHT>
<DIAM_HEIGHT>1.2,30.2</DIAM_HEIGHT>
<DIAM_HEIGHT>1.3,30.3</DIAM_HEIGHT>
<DIAM_HEIGHT>1.4,30.4</DIAM_HEIGHT>
<DIAM_HEIGHT>1.5,30.5</DIAM_HEIGHT>
</TREE>
<TREE NAME="oak">
<DIAM_HEIGHT>3.0,50.0</DIAM_HEIGHT>
<DIAM_HEIGHT>3.1,50.1</DIAM_HEIGHT>
<DIAM_HEIGHT>3.2,50.2</DIAM_HEIGHT>
<DIAM_HEIGHT>3.3,50.3</DIAM_HEIGHT>
</TREE>
<TREE NAME="maple">
<DIAM_HEIGHT>2.0,40.0</DIAM_HEIGHT>
</TREE>
</FOREST>
<FOREST NAME="caddo">
<TREE NAME="scrb">
<DIAM_HEIGHT>0.5,30.0</DIAM_HEIGHT>
</TREE>
</FOREST>
</ARBORINFO>
And here's the code that I got to partially work....
use XML::Simple;
$xml = new XML::Simple;
$phl = $xml->XMLin("./small.xml");
for(my $x=0;$x<$#{$phl->{FOREST}}+1;$x++)
{
$fname = $phl->{FOREST}[$x]{NAME};
$z = 0;
print "forest: $fname \n";
for(my $y=0;$y<$#{$phl->{FOREST}[$x]{TREE}}+1;$y++)
{
$tname = $phl->{FOREST}[$x]{TREE}[$y]{NAME};
print " tree: $tname\n";
for(my $z=0;$z<$#{$phl-> {FOREST}[$x]{TREE}[$y]{DIAM_HEIGHT}}+1;$
z++)
{
$tdata = $phl-> {FOREST}[$x]{TREE}[$y]{DIAM_HEIGHT}[$z];
print " tdata: $tdata\n";
}
}
}
And this is what I got...
forest: olympic
tree: spruce
tdata: 4.0,70.0
tdata: 4.1,70.1
tdata: 4.2,70.2
tree: redwood
tdata: 5.0,80.0
tdata: 5.1,80.1
tdata: 5.2,80.2
tdata: 5.3,80.3
tree: pine
tdata: 3.0,60.0
tdata: 3.1,60.1
forest: acadia
tree: birch
tdata: 1.0,30.0
tdata: 1.1,30.1
tdata: 1.2,30.2
tdata: 1.3,30.3
tdata: 1.4,30.4
tdata: 1.5,30.5
tree: oak
tdata: 3.0,50.0
tdata: 3.1,50.1
tdata: 3.2,50.2
tdata: 3.3,50.3
tree: maple
forest: caddo
Not an ARRAY reference at tryxml.pl line 12.
2 things to note....
1) It didn't pick up on the sole "maple" tree in "acadia"
2) It outright croaked when it tried to reference the sole tree in
"caddo" as an array.
I'm wide open as to suggestions on how to accomplish this. If foreach
can be used somehow, I'm wide open to that.
Any ideas?
Thanks in Advance
-dave
| |
| Rob Dixon 2007-04-03, 7:02 pm |
| Gauthier, Dave wrote:
> Hi:
>
>
>
> I'm trying to construct something like a nested for loop structuire to
> read the data out of an XML file using XML::Simple.
>
> Here's the sample xml...
>
>
>
> <?xml version='1.0' encoding='ISO-8859-1' ?>
>
> <ARBORINFO NAME="Info about Trees">
>
> <FOREST NAME="olympic">
>
> <TREE NAME="spruce">
>
> <DIAM_HEIGHT>4.0,70.0</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>4.1,70.1</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>4.2,70.2</DIAM_HEIGHT>
>
> </TREE>
>
> <TREE NAME="redwood">
>
> <DIAM_HEIGHT>5.0,80.0</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>5.1,80.1</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>5.2,80.2</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>5.3,80.3</DIAM_HEIGHT>
>
> </TREE>
>
> <TREE NAME="pine">
>
> <DIAM_HEIGHT>3.0,60.0</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>3.1,60.1</DIAM_HEIGHT>
>
> </TREE>
>
> </FOREST>
>
> <FOREST NAME="acadia">
>
> <TREE NAME="birch">
>
> <DIAM_HEIGHT>1.0,30.0</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>1.1,30.1</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>1.2,30.2</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>1.3,30.3</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>1.4,30.4</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>1.5,30.5</DIAM_HEIGHT>
>
> </TREE>
>
> <TREE NAME="oak">
>
> <DIAM_HEIGHT>3.0,50.0</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>3.1,50.1</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>3.2,50.2</DIAM_HEIGHT>
>
> <DIAM_HEIGHT>3.3,50.3</DIAM_HEIGHT>
>
> </TREE>
>
> <TREE NAME="maple">
>
> <DIAM_HEIGHT>2.0,40.0</DIAM_HEIGHT>
>
> </TREE>
>
> </FOREST>
>
> <FOREST NAME="caddo">
>
> <TREE NAME="scrb">
>
> <DIAM_HEIGHT>0.5,30.0</DIAM_HEIGHT>
>
> </TREE>
>
> </FOREST>
>
> </ARBORINFO>
>
>
>
>
>
> And here's the code that I got to partially work....
>
>
>
>
>
> use XML::Simple;
>
>
>
> $xml = new XML::Simple;
>
> $phl = $xml->XMLin("./small.xml");
>
>
>
> for(my $x=0;$x<$#{$phl->{FOREST}}+1;$x++)
>
> {
>
> $fname = $phl->{FOREST}[$x]{NAME};
>
> $z = 0;
>
> print "forest: $fname \n";
>
>
>
> for(my $y=0;$y<$#{$phl->{FOREST}[$x]{TREE}}+1;$y++)
>
> {
>
> $tname = $phl->{FOREST}[$x]{TREE}[$y]{NAME};
>
> print " tree: $tname\n";
>
>
>
> for(my $z=0;$z<$#{$phl-> {FOREST}[$x]{TREE}[$y]{DIAM_HEIGHT}}+1;$
z++)
>
> {
>
> $tdata = $phl-> {FOREST}[$x]{TREE}[$y]{DIAM_HEIGHT}[$z];
>
> print " tdata: $tdata\n";
>
> }
>
> }
>
> }
>
>
>
>
>
> And this is what I got...
>
>
>
> forest: olympic
>
> tree: spruce
>
> tdata: 4.0,70.0
>
> tdata: 4.1,70.1
>
> tdata: 4.2,70.2
>
> tree: redwood
>
> tdata: 5.0,80.0
>
> tdata: 5.1,80.1
>
> tdata: 5.2,80.2
>
> tdata: 5.3,80.3
>
> tree: pine
>
> tdata: 3.0,60.0
>
> tdata: 3.1,60.1
>
> forest: acadia
>
> tree: birch
>
> tdata: 1.0,30.0
>
> tdata: 1.1,30.1
>
> tdata: 1.2,30.2
>
> tdata: 1.3,30.3
>
> tdata: 1.4,30.4
>
> tdata: 1.5,30.5
>
> tree: oak
>
> tdata: 3.0,50.0
>
> tdata: 3.1,50.1
>
> tdata: 3.2,50.2
>
> tdata: 3.3,50.3
>
> tree: maple
>
> forest: caddo
>
> Not an ARRAY reference at tryxml.pl line 12.
>
>
>
> 2 things to note....
>
> 1) It didn't pick up on the sole "maple" tree in "acadia"
>
> 2) It outright croaked when it tried to reference the sole tree in
> "caddo" as an array.
>
>
>
> I'm wide open as to suggestions on how to accomplish this. If foreach
> can be used somehow, I'm wide open to that.
First of all, put
use strict;
use warnings;
at the beginning of your code and declare all your variables with 'my'.
This will help you avoid most of the common mistakes.
Your error occurs because forest 'caddo' contains only a single tree
and is represented by XML::Simple as a single anonymous hash instead
of an array of hashes like the others. To fix it change your parsing
line to
my $phl = $xml->XMLin('./small.xml', ForceArray => 1);
which will put this hash into an array just like the others.
HTH,
Rob
| |
| Rob Dixon 2007-04-19, 6:58 pm |
| Dave Gauthier wrote:
>
> Going back a couple w s regarding the nested foreach to read out nested
> data using XML::Simple, your solution works great, Thanks !
>
> But a new requirement is that I sort the output
>
>
> foreach (@{$xml->{FOREST}}) {
>
> print $_->{NAME}, ":\n";
>
> foreach (@{$_->{TREE}}) {
>
> print "\t", $_->{NAME}, ":\n";
>
> foreach (@{$_->{DIAM_HEIGHT}}) {
>
> print "\t\ttdata:\t", $_, "\n";
>
> }
>
> }
>
> }
>
>
> To be painfully honest, I'm not sure what's going on inside the...
>
> {@{$xml->{FOREST}}} (for example). Is there some sort of casting
> going on
> here? I tried to "sort" the list, and it ran, but the output was not
> sorted. I suspect that it's sortig pointers and not values :-(
>
> Any help would be appreciated.
May I encourage you to use an XML module other than XML::Simple, if that is
possible? XML::LibXML, for instance, allows you to use both XPath and the
Document Object Model to access the XML data, and makes for a more readable
program. You don't say what sorts you need to do on your output, but the program
below sorts the data by forest name and tree name as an example.
I hope this helps,
Rob
use strict;
use warnings;
use XML::LibXML;
my $parser = XML::LibXML->new;
$parser->keep_blanks(0);
my $doc = $parser->parse_fh(\*DATA);
sub byname {
$a->getAttribute('NAME') cmp $b->getAttribute('NAME');
}
my @forests = $doc->findnodes('/ARBORINFO/FOREST');
foreach my $forest (sort byname @forests) {
print $forest->getAttribute('NAME'), ":\n";
foreach my $tree (sort byname $forest->childNodes) {
print " ", $tree->getAttribute('NAME'), ":\n";
foreach my $size ($tree->childNodes) {
print " ", $size->textContent, "\n";
}
}
}
__DATA__
<?xml version='1.0' encoding='ISO-8859-1' ?>
<ARBORINFO NAME="Info about Trees">
<FOREST NAME="olympic">
<TREE NAME="spruce">
<DIAM_HEIGHT>4.0,70.0</DIAM_HEIGHT>
<DIAM_HEIGHT>4.1,70.1</DIAM_HEIGHT>
<DIAM_HEIGHT>4.2,70.2</DIAM_HEIGHT>
</TREE>
<TREE NAME="redwood">
<DIAM_HEIGHT>5.0,80.0</DIAM_HEIGHT>
<DIAM_HEIGHT>5.1,80.1</DIAM_HEIGHT>
<DIAM_HEIGHT>5.2,80.2</DIAM_HEIGHT>
<DIAM_HEIGHT>5.3,80.3</DIAM_HEIGHT>
</TREE>
<TREE NAME="pine">
<DIAM_HEIGHT>3.0,60.0</DIAM_HEIGHT>
<DIAM_HEIGHT>3.1,60.1</DIAM_HEIGHT>
</TREE>
</FOREST>
<FOREST NAME="acadia">
<TREE NAME="birch">
<DIAM_HEIGHT>1.0,30.0</DIAM_HEIGHT>
<DIAM_HEIGHT>1.1,30.1</DIAM_HEIGHT>
<DIAM_HEIGHT>1.2,30.2</DIAM_HEIGHT>
<DIAM_HEIGHT>1.3,30.3</DIAM_HEIGHT>
<DIAM_HEIGHT>1.4,30.4</DIAM_HEIGHT>
<DIAM_HEIGHT>1.5,30.5</DIAM_HEIGHT>
</TREE>
<TREE NAME="oak">
<DIAM_HEIGHT>3.0,50.0</DIAM_HEIGHT>
<DIAM_HEIGHT>3.1,50.1</DIAM_HEIGHT>
<DIAM_HEIGHT>3.2,50.2</DIAM_HEIGHT>
<DIAM_HEIGHT>3.3,50.3</DIAM_HEIGHT>
</TREE>
<TREE NAME="maple">
<DIAM_HEIGHT>2.0,40.0</DIAM_HEIGHT>
</TREE>
</FOREST>
<FOREST NAME="caddo">
<TREE NAME="scrb">
<DIAM_HEIGHT>0.5,30.0</DIAM_HEIGHT>
</TREE>
</FOREST>
</ARBORINFO>
**OUTPUT**
acadia:
birch:
1.0,30.0
1.1,30.1
1.2,30.2
1.3,30.3
1.4,30.4
1.5,30.5
maple:
2.0,40.0
oak:
3.0,50.0
3.1,50.1
3.2,50.2
3.3,50.3
caddo:
scrb:
0.5,30.0
olympic:
pine:
3.0,60.0
3.1,60.1
redwood:
5.0,80.0
5.1,80.1
5.2,80.2
5.3,80.3
spruce:
4.0,70.0
4.1,70.1
4.2,70.2
|
|
|
|
|