http://qs321.pair.com?node_id=169918

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

I am new to Perl and attempting to parse XML into an array of hashes using XML::Parser.
The script parses each child element of a <story> element into a hash. A reference to the hash is pushed on an array. When printing out the array of hashes, only the values from the last element parsed are displayed.
Overwriting the global hash is probably causing the problem.
I would like to either:
Create a lexically scoped hash variable but I am having difficulty due to the event driven XML::Parser subs (StartTag, EndTag, and Text)
Effectively deal with the global hash variable (by redefining it?)
thanks in advance monks -
use strict; use XML::Parser; use LWP::Simple; my @curr; my @stories; # global hash my %story; my $xml = get("http://www.slashdot.org/slashdot.xml"); die "Failed to obtain xml " unless defined($xml); my $p = XML::Parser->new(Style => 'Stream'); $p->parse($xml); # print out the results foreach my $hashref (@stories) { print "$hashref->{title}\n"; } # StartTag - called when the start of an XML tag is found sub StartTag { my($p, $tag) = @_; push @curr, $tag; } # EndTag - called when the end of a XML tag is seen sub EndTag { my($p, $tag) = @_; # pushes the hash ref onto the array if ($tag eq 'story') { push @stories, \%story; } pop @curr; } # Text - called when text data is encountered sub Text { unless ($curr[-1] eq 'story') { $story{$curr[-1]} = $_; } }
Example of a story element
<story> <title>Debian And WineX</title> <url>http://slashdot.org/article.pl?sid=02/05/28/1515220</url> <time>2002-05-28 18:25:01</time> <author>Hemos</author> ... </story>

Replies are listed 'Best First'.
Re: Problem with a Ref to a global variable while using XML::Parser
by clintp (Curate) on May 28, 2002 at 23:50 UTC
    I think you answered your own question (Effectively deal with the global hash variable (by redefining it?)). Can't you just let the last thing on @stories be the hash reference and not even deal with the hash?
    sub StartTag { # Other stuff okay... but add.. if ($tag eq 'story') { push @stories, {}; } } # This subroutine is mostly superflous now I think... sub EndTag { pop @curr; } sub Text { unless ($curr[-1] eq 'story') { $stories[-1]->{$curr[-1]} = $_; } }
    And that might do it. (untested)
      And that did it clintp!
      The script now works as expected.
      And thanks to crazyinsomniac for the recommendations.
(crazyinsomniac: perlref) Re: Problem with a Ref to a global variable while using XML::Parser
by crazyinsomniac (Prior) on May 29, 2002 at 00:24 UTC
    First, I reccomend you sit down and read perldata,perlref, and skim through perllol and perldsc, and then consider this piece of code
    #!/usr/bin/perl -w use strict; $\="\n"; my %jar = qw( a b c d); my @ppp = ( \%jar ); print a => $jar{a}; print a => $ppp[0]->{a}; print c => $jar{c}; print c => $ppp[0]->{c}; $jar{a}='gork'; $jar{c}='gork'; push @ppp, \%jar; print a => $jar{a}; print a => $ppp[0]->{a}; print a => $ppp[1]->{a}; print c => $jar{c}; print c => $ppp[0]->{c}; print c => $ppp[1]->{c}; { my %jar = ( a => shark => c => shark => 1 => 2 ); push @ppp, \%jar; } print a => $jar{a}; print a => $ppp[0]->{a}; print a => $ppp[1]->{a}; print a => $ppp[2]->{a}; print c => $jar{c}; print c => $ppp[0]->{c}; print c => $ppp[1]->{c}; print c => $ppp[2]->{c};
    Basically, all you're pushing onto your array, is the same hashref over and over and over again, whose values you redefine, over and over and over and over again.

    Now your hash is not a global, even though you kind of treat it as such (globals live in the package namespace, they aren't declared with my, they're declared with use vars '%global', or our(%global), or %package::name::global).

    Before you push @stories, \%story, you could simply create a new hash, and push it's reference onto @stories. Example

    use strict; $\="\n"; my %f = 1..4; print \%f; my %b = %f; print \%b; print they => are => not => equal => chr(33) if \%b ne \%f ;

     
    ______crazyinsomniac_____________________________
    Of all the things I've lost, I miss my mind the most.
    perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"