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

Hello, I'm trying to parse some attributes from my XML-file and count the different result attributes. I have read some examples but I can't get my code to work properly. When running my script I get a error massage "Not an ARRAY reference" Could some one help me?
<exec> <dut> <tc id="001.001" result="Passed" <ts .../> ... </tc> <tc id="002.001" result="Failed" <ts .../> ... </tc> ... </dut> </exec> ### use XML::Simple; my $Macaddress = param("macaddress"); my $conf = "/home/ember/devices/$Macaddress/$Macaddress.xml"; # create object my $xmlfile = new XML::Simple (ForceArray=>1); $xml = $xmlfile->XMLin($conf); $Passed = 0; $Warnings = 0; $Failed = 0; foreach my $TCresult (@{$xml->{tc}}){ if ($TCresult->{result} eq 'Passed'){$Passed++;} if ($TCresult->{result} eq 'Warnings'){$Warnings++;} if ($TCresult->{result} eq 'Failed'){$Failed++;} }

Replies are listed 'Best First'.
Re: XML::Simple parse attributes
by hipowls (Curate) on Jan 29, 2008 at 10:21 UTC

    When faced by these sort of problems it is a good practice to dump out the data structure you think you are dealing with, I use YAML, others swear by Data::Dumper. Which ever you choose it will help.

    One problem is that $xml doesn't have a key called tc, it has a key called dut, dumping out $xml would have shown that. The other is that you want to use result as a key but XML::Simple will use the value of the first attribute as a key. This is some code that works on the example provided. I don't know if it will work on the full set of data.

    use XML::Simple; my $XML_string=<<'XML'; <exec> <dut> <tc id="001.001" result="Passed"> <ts/> </tc> <tc id="002.001" result="Failed"> <ts/> </tc> <tc id="003.001" result="Warnings"> <ts/> </tc> </dut> </exec> XML # create object my $xmlfile = XML::Simple->new(KeyAttr => {result => 'result'} ); my $xml = $xmlfile->XMLin($XML_string); my $Passed = 0; my $Warnings = 0; my $Failed = 0; print Dump $XML; foreach my $TCresult (@{$xml->{dut}{tc}}){ print Dump $TCresult; if ($TCresult->{result} eq 'Passed'){$Passed++;} if ($TCresult->{result} eq 'Warnings'){$Warnings++;} if ($TCresult->{result} eq 'Failed'){$Failed++;} } print "Passed: $Passed\n"; print "Warnings: $Warnings\n"; print "Failed: $Failed\n";

    Example YAML output

    --- dut: tc: - id: 001.001 result: Passed ts: {} - id: 002.001 result: Failed ts: {} - id: 003.001 result: Warnings ts: {}
    Example Data::Dumper output (indent set to 4 spaces)
    $VAR1 = [ { 'dut' => { 'tc' => { '002.001' => { 'ts' => {}, 'result' => 'Failed' }, '001.001' => { 'ts' => {}, 'result' => 'Passed' }, '003.001' => { 'ts' => {}, 'result' => 'Warnings' } } } } ];

      Hello your code works fine but when using my real XML-file somethings goes wrong.
      <exec> <dut frimware="4.40.1" ipaddress="171.0.0.234" macaddress="00408C727 +B40" model="AXIS 207W" /> <tc id="001.001" objective="The objective of this test case is to ve +rify that it is possible to upgrade the DUT's firmware by uploading a + firmware image file to the DUT using ftp when the DUT is connected t +o a network with 100 Mbit/s speed and that the DUT meet the PRS requi +rement." result="Passed" time="1201248021" version="0.01"> <ts id="001.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248021" /> <ts id="001.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248031" /> </tc> <tc id="002.001" objective="The objective is to verify that the DUT +can handle the maximum number of events for at least a week" result=" +Failed" time="1201248031" version="0.01"> <ts id="002.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248031" /> <ts id="002.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248041" /> </tc> <tc id="004.001" objective="The objective is to verify that the HTTP + traffic is marked correctly" result="Passed" time="1201248041" versi +on="0.01"> <ts id="004.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248041" /> <ts id="004.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248051" /> </tc> <tc id="005.001" objective="Verify that the CA certificate can be up +loaded" result="Passed" time="1201248051" version="0.01"> <ts id="005.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248051" /> <ts id="005.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248061" /> </tc> <tc id="006.001" objective="Verify that the DUT can be configured wi +th the Axis Setup Tool to connect to a wireless network" result="Fail +ed" time="1201248061" version="0.01"> <ts id="006.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248061" /> <ts id="006.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248071" /> </tc> <tc id="007.001" objective="The objective of this test case is to ve +rify that the DUT is compatible with Axis IP utility" result="Passed" + time="1201248071" version="0.01"> <ts id="007.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248071" /> <ts id="007.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248081" /> </tc> <tc id="008.001" objective="The objective of this test case is to ve +rify that the DUT can send live image and that all the links are corr +ect" result="Passed" time="1201248081" version="0.01"> <ts id="008.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248081" /> <ts id="008.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248091" /> </tc> <tc id="009.001" objective="Verify that it is possible to install th +e AXIS Media Control Embedded (AMC Embedded) on a computer where it h +as never been installed before" result="Passed" time="1201248091" ver +sion="0.01"> <ts id="009.001.001" description="log in using FTP" expected_resul +t="login granted" result="Passed" time="1201248091" /> <ts id="009.001.002" description="start upload" expected_result="u +pload finished" result="Passed" time="1201248101" /> </tc> </exec>
Re: XML::Simple parse attributes
by shmem (Chancellor) on Jan 29, 2008 at 10:30 UTC
    From your example xml structure, it looks like the tc elements live in a hash keyed with their 'id' value:
    my $tc = $xml->{dut}->[0]->{tc}; foreach my $id (keys %$tc) { if ($tc->{$id}->{result} eq 'Passed'){$Passed++;} if ($tc->{$id}->{result} eq 'Warnings'){$Warnings++;} if ($tc->{$id}->{result} eq 'Failed'){$Failed++;} }

    Dumping the $xml object with e.g. Data::Dumper helps in such a case.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: XML::Simple parse attributes
by moritz (Cardinal) on Jan 29, 2008 at 10:22 UTC
    Your XML sample isn't even valid (missing >s), and when I corrected that your sample program worked without error.

    So please provide a sample file which actually produces the error.

    Perhaps you'll need ForceArray=>['tc'] instead of ForceArray=>1.

Re: XML::Simple parse attributes
by olus (Curate) on Jan 29, 2008 at 11:15 UTC
Re: XML::Simple parse attributes
by Jenda (Abbot) on Feb 06, 2008 at 00:42 UTC
    use XML::Rules; my %results; my $parser=XML::Rules->new( rules => [ tc => sub { $results{ $_[1]->{result} }++; return }, _default => '', ], ); $parser->parse($conf); foreach my $result (sort keys %results) { print "$result: $results{$result}\n"; }

    Why build a structure containing all the data from the XML if all you want to know are the counts of the different result attributes of the <tc> tag?