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

Hi, I try to get the informations contained in an xml file.

The xml file :
<?xml version='1.0'?> <Targets> <main_target> <main_target_name>Instance</main_target_name> <target> <target_name>estspc_netlist</target_name> <target_options>CELLS</target_options> </target> <target> <target_name>ibitmap</target_name> <target_options>CELLS</target_options> <target_options>STACK_MODE</target_options> </target> <target> <target_name>liberty</target_name> <target_options>STACK_MODE</target_options> <target_options>VT_MODE</target_options> <sub_target> <sub_target_name>nldm_liberty</sub_target_name> </sub_target> <sub_target> <sub_target_name>ccs_liberty</sub_target_name> <sub_target_options>CELLS</sub_target_options> <sub_target_options>VT_MODE</sub_target_options> </sub_target> </target> </main_target> <main_target> <main_target_name>Validation</main_target_name> <target> <target_name>bmval</target_name> <target_options>VT_MODE</target_options> <sub_target> <sub_target_name>bmval_lvs</sub_target_name> <sub_target_options>VT_MODE</sub_target_options> </sub_target> <sub_target> <sub_target_name>bmval_sim</sub_target_name> <sub_target_options>STACK_MODE</sub_target_options> <sub_target_options>VT_MODE</sub_target_options> </sub_target> </target> </main_target> </Targets>
My code :
#!/usr/bin/env perl # use module use XML::Simple; use Data::Dumper; # create object $xml = new XML::Simple; # read XML file $xmlData = $xml->XMLin("./xuo.xml", forcearray => 1); # print output # print Dumper($data); my $main_target = '' ; my $target = '' ; my $sub_target = '' ; my $main_target_name = '' ; my $target_name = '' ; foreach $main_target (@{$xmlData->{main_target}}) { $main_target_name = @{$main_target->{main_target_name}}[0] ; print "Main Target Name : ", $main_target_name, "\n" ; foreach $target (@{$main_target->{target}}) { $target_name = @{$target->{target_name}}[0] ; print " Target Name : ", $target_name, "\n"; # if (exists $main_target->{target}->{sub_target}) # { foreach $sub_target (@{$main_target->{target}->{sub_target +}}) { print " Sub Target Name : ", $sub_target, "\n"; } # } } } exit ;
If I comment out the lines foreach $sub_target...
then I get something correct :

Main Target Name : Instance
Target Name : estspc_netlist
Target Name : ibitmap
Target Name : liberty
Main Target Name : Validation
Target Name : bmval

But the "sub_target" is not always defined in the "target" fields of the xml file. This is the reason why it fails. But I don't know how to test the existence of this "sub_target" field to print only when this field do exist.

Do you know how to do ?

Regards.

Xuo.

Replies are listed 'Best First'.
Re: I get "not a HASH reference" when parsing an XML file
by 1nickt (Canon) on Apr 10, 2020 at 17:49 UTC

    Hi,

    Try changing

    if (exists $main_target->{target}->{sub_target})
    to
    if (exists $main_target->{$target}->{sub_target}) # ^

    Hope this helps!


    The way forward always starts with a minimal test.
Re: I get "not a HASH reference" when parsing an XML file
by choroba (Cardinal) on Apr 10, 2020 at 23:43 UTC
    XML::Simple itself tells you not to use it.

    You can use XML::LibXML instead:

    #! /usr/bin/perl use warnings; use strict; use feature qw{ say }; use XML::LibXML; my $dom = 'XML::LibXML'->load_xml(location => shift); for my $main_target ($dom->findnodes('/Targets/main_target')) { say "Main Target Name: ", $main_target->findvalue('main_target_nam +e'); for my $target ($main_target->findnodes('target')) { my $target_name = $target->findvalue('target_name'); say "\tTarget Name: $target_name"; say "\t\tSub Target Name: ", $_->findvalue('sub_target_name') for $target->findnodes('sub_target'); } }

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: I get "not a HASH reference" when parsing an XML file
by BillKSmith (Monsignor) on Apr 11, 2020 at 21:05 UTC
    choroba has already pointed out that you have made a poor choice of modules. One of the problems with XML::Simple is that it is often difficult to dereference the complex data structure that it creates. That is true in your case. You have many more dereferencing mistakes than you realize. Your program gives up when it tries to execute the first one. The common theme in all your errors is your failure to take advantage of the dereferencing that has already been done. Here is a corrected version.
    #!/usr/bin/env perl use strict; use warnings; # use module use XML::Simple; use Data::Dumper; # create object my $xml = new XML::Simple; # read XML file my $xmlData = $xml->XMLin( "./xuo.xml", forcearray => 1 ); # print output # print Dumper($data); #print Dumper($xmlData); my $main_target = ''; my $target = ''; my $sub_target = ''; my $main_target_name = ''; my $target_name = ''; foreach $main_target ( @{ $xmlData->{main_target} } ) { #$main_target_name = @{$main_target->{main_target_name}}[0] ; $main_target_name = $main_target->{main_target_name}[0]; print "Main Target Name : ", $main_target_name, "\n"; foreach $target ( @{ $main_target->{target} } ) { #$target_name = ${$target->{target_name}}[0] ; $target_name = $target->{target_name}[0]; print " Target Name : ", $target_name, "\n"; #if ( exists $main_target->{target}->{sub_target} ) if ( exists $target->{sub_target} ) { #foreach $sub_target ( @{ $main_target->{target}->{sub_tar +get} } ) foreach $sub_target ( @{ $target->{sub_target} } ) { #print " Sub Target Name : ", $sub_target, "\n"; print " Sub Target Name : ", $sub_target->{sub_target_name}[0], "\n"; } } } } exit;

    OUTPUT:

    Main Target Name : Instance Target Name : estspc_netlist Target Name : ibitmap Target Name : liberty Sub Target Name : nldm_liberty Sub Target Name : ccs_liberty Main Target Name : Validation Target Name : bmval Sub Target Name : bmval_lvs Sub Target Name : bmval_sim
    Bill
Re: I get "not a HASH reference" when parsing an XML file
by Anonymous Monk on Apr 13, 2020 at 09:33 UTC
    Hi,

    Thank you to all of you for your answers.
    I've tested the 3 scripts (at home) and all of them work, except the 1st one which ends correctly but does not display the sub-target names.
    If everybody agrees on the fact that it is better to use the LibXML library, then I'll use it. If it is not already installed on the stations at work, I'll request for it.
    I didn't know the "say" feature. I'll have a look at it to better understand what it does.

    Regards.

    Xuo.
      If everybody agrees on the fact that it is better to use the LibXML library

      Definitely don't use XML::Simple! Next to XML::LibXML, there are other good XML modules out there, such as XML::Twig or XML::Rules, each with strengths in certain areas, but XML::LibXML is definitely the most general-purpose. If you're not sure, use XML::LibXML.

      I didn't know the "say" feature. I'll have a look at it to better understand what it does.

      say is equivalent to print with a newline appended. It's available on Perl 5.10 and up when you say use 5.010; or use feature 'say'; at the top of the script.

        Hi,

        Thank you for the explanations. I've just started to search about the 'say' feature, that I've already an answer !!!

        Regards.

        Xuo.