Shortcutting a SAX parser
Michael Ludwig <[email protected]>
| Newsgroups | gmane.comp.lang.perl.xml |
|---|---|
| Message-ID | <[email protected]> |
I have thousands of documents each of which contains some information
in the attributes of the document element. I'm only interested in this
particular information and not in the rest of the document. So I wrote a
SAX parser to extract only the relevant information and not bother to
examine the rest of the document.
package MySAXHandler;
use strict;
use warnings;
use base 'XML::SAX::Base';
sub start_element {
my( $self, $elm) = @_;
return unless $self->{searching};
if ( $elm->{LocalName} eq $self->{elm_searched} ) {
$self->{searching} = 0; # data found, may stop searching
$self->{callback}->( $elm); # collect data
}
}
sub new {
my( $pkg, %opt) = @_;
my $self = $pkg->SUPER::new( %opt);
$self->{searching} = 1; # we haven't yet found what we're looking for
$self->{elm_searched} ||= 'TVChannel'; # looking for this by default
return bless $self, $pkg;
}
sub reset {
my $self = shift;
$self->{searching} = 1; # so we may reuse the same parser
}
# Now a very simple main program.
package main;
use strict;
use warnings;
use XML::SAX;
# collect certain data by printing it to STDOUT
my $callback = sub {
my $elm = shift;
my $att_id = $elm->{Attributes}{'{}TVChannelID'};
my $att_name = $elm->{Attributes}{'{}TVChannelName'};
print $att_id->{Value}, "\t", $att_name->{Value}, "\n";
};
my %handler_opt = (
# elm_searched => 'Gurke', # rely on default value
callback => $callback,
);
my $handler = MySAXHandler->new( %handler_opt);
my $parser = XML::SAX::ParserFactory->parser( Handler => $handler);
# read file names from STDIN (XML documents, hopefully) and parse them
while ( <> ) {
chomp;
$parser->parse_uri( $_);
$handler->reset;
}
So this works, but still the documents are parsed in their entirety.
This is not necessary - I know they're well-formed, and there is nothing
down the pipe of interest here.
It occurred to me that I could shortcut the process by throwing an
exception to terminate the parser once the data is harvested, catch the
exception, and then move on to the next document. This is just a trivial
change to the code:
(1) die in start_element after having collected the data
(2) protect the parser invocation: eval { $parser->parse_uri( $_) };
As the data I need is at the very start of the document, this is
significantly faster, even though the documents are only 35 KB on
average.
Is dying the way to go here? Or is there a more elegant and recommended
way to terminate the parser? Other thoughts or comments?
Michael Ludwig
_______________________________________________
Perl-XML mailing list
[email protected]
To unsubscribe: http://listserv.ActiveState.com/mailman/mysubs