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

Seem to be having an issue in trying to walk hash of hashs. It seemed to be working but now not working. I uninstalled perl and installed again (activestate 5.18 64bit)

Just updated to perl 5.20.2.2002 64bit - still same result.

#!perl use XML::Simple; use Data::Dumper; my $xmlSimple = new XML::Simple(KeepRoot => 1); my $professionsXML = $xmlSimple->XMLin("data/misc/test.xml"); print Dumper($professionsXML); print qq(0.1: $professionsXML{'profs'}{'name'}\n); print qq(0.2: $professionsXML{'profs'}{'profcats'}{'name'}\n); foreach my $key (keys %{$professionsXML}) { print qq(1: $key\n); foreach my $key2 (keys %{$professionsXML{$key}}) { print qq(2: $key2\n); foreach my $key3 (keys %{$professionsXML{$key}{$key2}}) { print qq(3: $key3\n); foreach my $key4 (keys %{$professionsXML{$key}{$key2}{$key +3}}) { print qq(4: $key4\n); } } } } exit(0);

XML Source

<profs> <name>prof</name> <profcats> <name>cat</name> <profcatgroups> <name>group</name> <profcatgroup> <name>prof1</name> </profcatgroup> </profcatgroups> </profcats> </profs>

Output

C:\Utils\Apache\Apache-httpd-2.2\cgi-bin>perl generate03.pl $VAR1 = { 'profs' => { 'profcats' => { 'profcatgroups' => { 'name' => 'group +', 'profcatgroup' = +> { + 'name' => 'prof1' +} }, 'name' => 'cat' }, 'name' => 'prof' } }; 0.1: 0.2: 1: profs 2: profcats C:\Utils\Apache\Apache-httpd-2.2\cgi-bin>

As you can see from output, it I can't seem to manually walk tree, and in using loops to walk try it seems to get hungup after profcats.

Replies are listed 'Best First'.
Re: Walking Hash of hash issue?
by Corion (Patriarch) on Oct 23, 2015 at 16:51 UTC

    The problem is not in Perl, but in your use of variables. Once you allow Perl to tell you what's wrong, it becomes more obvious - add use strict; to your program.

    The whole situation is not helped by an experimental feature of Perl that allowed (hash) references to be used like hashes. This feature is still experimental but I think it's on its way out of Perl again.

    You're reading your XML into $professionsXML but then you're accessing %professionsXML.

    The following script goes through your data structure and whenever it can go deeper in the structure, it does so. See also ref.

    #!perl use strict; use XML::Simple; use Data::Dumper; my $xmlSimple = new XML::Simple(KeepRoot => 1); my $xml = join "", <DATA>; my $professionsXML = $xmlSimple->XMLin($xml); print Dumper($professionsXML); print qq(0.1: $professionsXML->{'profs'}->{'name'}\n); print qq(0.2: $professionsXML->{'profs'}->{'profcats'}->{'name'}\n); foreach my $key (keys %{$professionsXML}) { print qq(1: $key\n); foreach my $key2 (keys %{$professionsXML->{$key}}) { print qq(2: $key2\n); if( ref $professionsXML->{$key}->{$key2} ) { foreach my $key3 (keys %{$professionsXML->{$key}->{$key2}} +) { print qq(3: $key3\n); if( ref $professionsXML->{$key}->{$key2}->{$key3}) { foreach my $key4 (keys %{$professionsXML->{$key}-> +{$key2}->{$key3}}) { print qq(4: $key4\n); } }; } }; } } exit(0); __DATA__ <profs> <name>prof</name> <profcats> <name>cat</name> <profcatgroups> <name>group</name> <profcatgroup> <name>prof1</name> </profcatgroup> </profcatgroups> </profcats> </profs>

    Please consider using something other than XML::Simple - it will cause you no end of headache in the long run. As soon as the structure of your XML is not easily guessable (for example, repeated elements in profcats, your code will break.

Re: Walking Hash of hash issue?
by Preceptor (Deacon) on Oct 23, 2015 at 16:44 UTC

    It may not seem like it, but this is actually an XY problem. "XML::Simple" is dirty bad and wrong. Try using XML::Twig instead. (I think as you said Activeperl, XML::LibXML is unavailable, which would be another good choice generally, and comes bundled with Strawberry perl)

    If you're trying to just print every node:

    #!/usr/bin/env perl use strict; use warnings; use Data::Dumper; use XML::Twig; my $twig = XML::Twig -> parsefile ( 'data/misc/test.xml' ); foreach my $node ( $twig -> get_xpath ( '//*' ) ) { print $node -> tag, ": ", $node -> text,"\n"; }

    Of course, this is a bit of an artificial scenario. You can do something similar to what you've got using "children" and "first_child" instead. But the real question is - what are you trying to accomplish?

    Because XML::Simple is probably not the right tool to be using - it has very few redeeming features. Despite the name, it's not simple to use, it can only cope with simple XML

    That's why it's officially discouraged, according to the module docs

    For the sake of an exact comparison with your code:

    print $twig -> root -> first_child_text('name'),"\n"; print $twig -> root -> first_child('profcats') -> first_child_text('na +me'),"\n"; foreach my $element ( $twig -> root -> children ) { print $element -> tag, ": ", $element -> text,"\n"; foreach my $sub ( $element -> children ) { print "\t",$sub -> tag, ": ", $sub -> text,"\n"; foreach my $subsub ( $sub -> children ) { print "\t\t",$subsub -> tag, ": ", $sub -> text,"\n"; foreach my $subsubsub ( $subsub -> children ) { print "\t\t\t", $subsubsub -> tag, ": ", $subsubsub -> +text,"\n"; } } } }

    But honestly - I don't think you really need to do that, and one of the alternatives would be better

    And whilst we're at it - turn on use strict; and use warnings;

Re: Walking Hash of hash issue?
by stevieb (Canon) on Oct 23, 2015 at 16:54 UTC

    use warnings; and in this case specifically, use strict; will point you to your errors. Correct those and you'll be much further along.

    You're not accessing the elements of the hash reference correctly (perl thinks you're trying to access a hash, not a reference to one). Use the -> dereference operator between the reference and the first key to distinguish it as a ref, eg: %{$professionsXML->{$key}}.

    Also, there are values in that structure that are not hashrefs, so you need to explicitly check for this. Here's (a messy) working example to show you what I mean by my statements. I most likely wouldn't have written it like this, but I wanted to point out where the mistakes are within the code structure you provided

    use warnings; use strict; use XML::Simple; use Data::Dumper; my $xmlSimple = new XML::Simple(KeepRoot => 1); my $professionsXML = $xmlSimple->XMLin("in.xml"); print Dumper($professionsXML); print qq(0.1: $professionsXML->{'profs'}{'name'}\n); print qq(0.2: $professionsXML->{'profs'}{'profcats'}{'name'}\n); foreach my $key (keys %{$professionsXML}) { next if ref $professionsXML->{$key} ne 'HASH'; print qq(1: $key\n); foreach my $key2 (keys %{$professionsXML->{$key}}) { next if ref $professionsXML->{$key}{$key2} ne 'HASH'; print qq(2: $key2\n); foreach my $key3 (keys %{$professionsXML->{$key}{$key2}}) { next if ref $professionsXML->{$key}{$key2}{$key3} ne 'HASH +'; print qq(3: $key3\n); foreach my $key4 (keys %{$professionsXML->{$key}{$key2}{$k +ey3}}) { print qq(4: $key4\n); } } } } exit(0);
      Thanks all for the comments/help. I have been very slack in using perl in the last 4-5 years, so when I saw the removal of the hash pointers I thought that was the new way to do it. I thought I tried swapping back, but must have had other errors at the time. I will take a look at the other packages. Again Greatly thanks for help!