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

I'm writing a package in OO style which use XML::Parser and its callbacks. I put the callbacks in the 'new' subroutine.
Package Report; sub new() { my $self = {}; bless($self); [...] $self->{tree} = (); my $p = new XML::Parser( Handlers => { Start => \&handle_start, Default => \&handle_char, } ); $p->parsefile($my_file); sub handle_char() { # ... update $self->{tree} } sub handle_start() { # ... update $self->{tree} print Dumper \$self->{tree}; #dumper 1 } print Dumper \$self->{tree}; #dumper 2 return $self; }
When I want to use it, it only works for the first :
my $r = new Report('file1'); #all is good $r = new Report('file2'); # Dumper1 shows the first tree updated with the data of the second # and dumper2 shows nothing (undef)
I see that the second object uses the same tree of the first object.
How can I do that code work? Or is there a better design?

Thanks for your wisdom.

Replies are listed 'Best First'.
Re: Objects, callbacks and XML::Parser
by Fletch (Bishop) on Dec 08, 2006 at 17:45 UTC

    The handlers aren't being called as methods on your instance. Instead perhaps you should pass an anonymous coderef which turns the callback into a method call?

    my $p = XML::Parser->new( Handlers => { Start => sub { $self->handle_start( @_ ) }, Default => sub { $self->handle_char( @_ ) }, }, );

    Additionally: be aware that defining subs nested inside of subs doesn't create lexically scoped subs; they're still members of the package in which they're declared, and in fact doing things this way can lead to strange warning messages (the infamous "variable will not stay shared" seen often in mod_perl circles; see "my () Scoped Variable in Nested Subroutines" for an explanation).

      that works perfectly. many thanks
Re: Objects, callbacks and XML::Parser
by Joost (Canon) on Dec 09, 2006 at 00:21 UTC
    Note that XM::Parser->new() only sets up handlers. It does not parse anything. That means that probably "dumper 1" should never give you any useful information.

    I also note that you appear to be using references to methods as the handlers, while XML::Parser expects non-method subroutines. The only reason this might sortof work (as in - not break immediately) is that you declare $self in the same scope as the methods. I might be mislead here, since I can't read the code you left out.

    In any case, the real problem is somewhere in the lines you left out.

    as a side note: using new ClassName has it's problems. If you want to be sure you call a class method instead of any random new() subroutine that happens to be in scope, use ClassName->new() instead.

    update: are you using use warnings or the -w switch? that should warn on sharing lexial context in named subroutines. In other words, you will have the $self from the first call to new() in the later calls. This is a known problem in perl, but it's also debatable what the correct solution should be. (since named subroutines are global objects). See this part of the mod-perl docs for more info