http://www.perlmonks.org?node_id=1022313

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

Hello folks!

Do you know XML::easytree? It's really good tool, but unfortunately I am a perl newbie and I can't get the content of the $tree out for my other purposes. I am really depressed, for last 4 days I couldn't come up with my own idea and the only example of what I want to do next I found is some kind of broken or what.

See:
#!/usr/bin/perl -w #use strict; use XML::Parser; use XML::Parser::EasyTree; use Data::Dumper; my $p=new XML::Parser(Style=>'EasyTree'); my $tree=$p->parsefile("test.xml"); #my $element; print_easy_tree($tree); sub print_easy_tree { my $node = shift; { if ($element->{type} eq 'e') { print "<$element->{name}>"; print_easy_tree($element->{content}); print "</$element->{name}>"; } elsif ($element->{type} eq 't') { print $element->{content}; } else { print "[IGNORED $element->{type}]"; } } }

When I try to run this code, I get error that $element is not initialized. I tried to repair it, but I don't know where and what. Please help poor stupid student monk and end his torture.

Replies are listed 'Best First'.
Re: How to get content of an XML::easytree output
by kcott (Archbishop) on Mar 07, 2013 at 23:49 UTC

    G'day Tworec,

    Welcome to the monastery.

    Firstly, I see you've commented out "use strict;" - uncomment this and also add "use warnings;". Removing the output of warnings does not remove the problem they're telling you about!

    You've commented out the declaration "my $element;". You assign nothing to $element. You then use $element as if it's a hashref - this is why you're getting the error message you report.

    Your print_easy_tree() subroutine assigns its argument to $node; you then make no further reference to $node! Each instance of $element in this function should probably be $node.

    I believe that should fix up your immediate problems. Here's some tips that you may find generally useful (i.e. not just for this code); the first two won't necessarily change how your code runs, but they should make it easier to read and understand:

    • Avoid indirect object syntax when instantiating: use Class->new(args) instead of new Class(args)
    • Avoid embedding complex dereferencing within interpolated strings, e.g. instead of print "<$element->{name}>";, use print '<', $element->{name}, '>';
    • The print function does not output newlines: you'll usually want print $output_line, "\n";. Alternatively, use the say function.

    -- Ken

      "e.g. instead of print "<$element->{name}>";, use print '<', $element->{name}, '>';

      Personally, I'd prefer:

      printf '<%s>', $element->{name};

      Update: just for the heck of it, this ain't too bad either:

      print "<$_>" for $element->{name};

      Using postfix for to temporarily alias $_ is an idiom I don't see used very much, but is quite cute, and in this case has no speed penalty (quite the opposite in fact)...

      use strict; use Benchmark ':all'; open $::dummy, ">", \$::data; cmpthese(100_000, { print_for => q[ $::data = ''; print {$::dummy} "<$_>" for "a" ], printf => q[ $::data = ''; printf {$::dummy} "<%s>", "a" ], }); __END__ Rate printf print_for printf 7663/s -- -79% print_for 36765/s 380% --

      (PS: kcott's follow-up was posted before this update, so please don't read it as necessarily endorsing the for postfix technique.)

      package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

        That, too, is good; it isolates the $element->{name} (++).

        Tworec states "I am a perl newbie" and "<$element->{name}>" didn't seem to scream clarity; in particular, the <$element-> bit.

        -- Ken

      First of all, thanks for your reply. I changed all variables $element to $node and errors I reported are gone. But unfortunately a new one came. Not a HASH reference at line: if ($node->{type} eq 'e'). I think it's because of the $tree variable is ARRAY, but there is the main problem. My brain cannot really understand how to go threw an array of hashes and print it's elements. Should I write something like
      if(ref($node) eq HASH) do the code I have elsif(ref($node) eq ARRAY) $node = shift;
      ?

        Something like the following (untested) skeleton code:

        sub print_easy_tree { my $node_array_ref = shift; for my $node (@$node_array_ref) { if ($node->{type} eq 'e') { ... } elsif ($node->{type} eq 't') { ... } else { ... } } }

        -- Ken

Re: How to get content of an XML::easytree output
by Anonymous Monk on Mar 07, 2013 at 23:42 UTC
    Can't be done, no sample input