=pod

=head1 NAME

SOAP::WSDL::Parser - How SOAP::WSDL parses XML messages

=head1 Which XML message does SOAP::WSDL parse ?

Naturally, there are two kinds of XML documents (or messages) SOAP::WSDL has 
to parse:

=over

=item * WSDL definitions

=item * SOAP messages

=back

=head1 Parser implementations

There are different parser implementations available for SOAP messages and 
WSDL definitions.

Historically, SOAP::WSDL used SAX for parsing XML. The SAX handlers were 
implemented as L<XML::LibXML|XML::LibXML> handlers, which also worked with 
L<XML::SAX::ParserFactory|XML::SAX::ParserFactory>.

Support for SAX and L<XML::LibXML|XML::LibXML> in SOAP::WSDL is discontinued 
for the following reasons:

=over

=item * Speed

L<XML::Parser::Expat|XML::Parser::Expat> is faster than 
L<XML::LibXML|XML::LibXML> - at least when optimized for speed.

High parsing speed is one of the key requirements for a SOAP toolkit - if XML 
serializing and (more important) deserializing are not fast enough, the whole 
toolkit is unusable.

=item * Availability

L<XML::Parser|XML::Parser> is more popular than L<XML::LibXML|XML::LibXML>.

=item * Stability

XML::LibXML is based on the libxml2 library. Several versions of 
libxml2 are known to have specific bugs. As a workaround, there are 
often several versions of libxml2 installed on one system. This may 
lead to problems on operating systems which cannot load more than 
one version of a shared library simultaneously.

XML::LibXML is also still under development, while XML::Parser has had time 
to stabilize.

=item * SOAP::Lite uses XML::Parser

L<SOAP::Lite|SOAP::Lite> uses L<XML::Parser|XML::Parser> if available. 
SOAP::WSDL should not require users to install both L<XML::Parser|XML::Parser> 
and L<XML::LibXML|XML::LibXML>.

=back

=head2 WSDL definitions parser

=over

=item * SOAP::WSDL::Expat::WSDLParser

A parser for WSDL definitions based on L<XML::Parser::Expat|XML::Parser::Expat>.

 my $parser = SOAP::WSDL::Expat::WSDLParser->new();
 my $wsdl = $parser->parse_file( $filename );

=back

=head2 SOAP messages parser

All SOAP message handler use class resolvers for finding out which class 
a particular XML element should be of, and type libs containing these classes.

=head3 Creating a class resolver

The easiest way for creating a class resolver is to run SOAP::WSDL's generator.

See wsdl2perl.pl 

The class resolver must implement a class method "get_class", which is passed 
a list ref of the current element's XPath (relative to Body), split by /.

This method must return a class name appropriate for a XML element.

A class resolver package might look like this:

 package ClassResolver;

 my %class_list = (
    'EnqueueMessage' => 'Typelib::TEnqueueMessage',
    'EnqueueMessage/MMessage' => 'Typelib::TMessage',
    'EnqueueMessage/MMessage/MRecipientURI' => 'SOAP::WSDL::XSD::Builtin::anyURI',
    'EnqueueMessage/MMessage/MMessageContent' => 'SOAP::WSDL::XSD::Builtin::string',
 );

 sub new { return bless {}, 'ClassResolver' };

 sub get_class {
    my $name = join('/', @{ $_[1] });
    return ($class_list{ $name }) ? $class_list{ $name }
        : warn "no class found for $name";
 };
 1;

=head3 Skipping unwanted items

Sometimes there's unneccessary information transported in SOAP messages.

To skip XML nodes (including all child nodes), just edit the type map for 
the message and set the type map entry to '__SKIP__'. 

In the example above, EnqueueMessage/StuffIDontNeed and all child elements 
are skipped.

 my %class_list = (
    'EnqueueMessage' => 'Typelib::TEnqueueMessage',
    'EnqueueMessage/MMessage' => 'Typelib::TMessage',
    'EnqueueMessage/MMessage/MRecipientURI' => 'SOAP::WSDL::XSD::Builtin::anyURI',
    'EnqueueMessage/MMessage/MMessageContent' => 'SOAP::WSDL::XSD::Builtin::string',
    'EnqueueMessage/StuffIDontNeed' => '__SKIP__',
    'EnqueueMessage/StuffIDontNeed/Foo' => 'SOAP::WSDL::XSD::Builtin::string',
    'EnqueueMessage/StuffIDontNeed/Bar' => 'SOAP::WSDL::XSD::Builtin::string',
 );

Note that only SOAP::WSDL::Expat::MessageParser implements skipping elements 
at the time of writing.

=head3 Creating type lib classes

Every element must have a correspondent one in the type library.

Builtin types should be resolved as SOAP::WSDL::XSD::Builtin::* classes

Creating a type lib is easy: Just run SOAP::WSDL's generator - it will 
create both a typemap and the type lib classes for a WSDL file.

Sometimes it is nessecary to create type lib classes by hand - not all 
WSDL definitions are complete.

For writing your own lib classes, see L<SOAP::WSDL::XSD::Typelib::Element>,
L<SOAP::WSDL::XSD::Typelib::ComplexType> and L<SOAP::WSDL::XSD::Typelib::SimpleType>.

=head3 Parser implementations

=over

=item * SOAP::WSDL::Expat::MessageParser

A L<XML::Parser::Expat|XML::Parser::Expat> based parser. This is the fastest 
parser for most SOAP messages and the default for SOAP::WSDL::Client.

=item * SOAP::WSDL::Expat::MessageStreamParser

A XML::Parser::ExpatNB based parser. Useful for parsing huge HTTP responses, 
as you don't need to keep everything in memory.

See L<SOAP::WSDL::Expat::MessageStreamParser|SOAP::WSDL::Expat::MessageStreamParser> 
for details.

=back

=head3 Performance

SOAP::WSDL::Expat::MessageParser is the fastest way of parsing SOAP messages 
into object trees and only slightly slower than converting them into hash 
data structures:

 Parsing a SOAP message with a length of 5962 bytes:
 SOAP::WSDL::Expat::MessageParser:
    3 wallclock secs ( 3.28 usr +  0.05 sys =  3.33 CPU) @ 60.08/s (n=200)
  
 SOAP::WSDL::SAX::MessageHandler (with raw XML::LibXML):   
   5 wallclock secs ( 4.95 usr +  0.00 sys =  4.95 CPU) @ 40.38/s (n=200)
 
 XML::Simple (XML::Parser):
   3 wallclock secs ( 2.36 usr +  0.03 sys =  2.39 CPU) @ 83.65/s (n=200)
 
 XML::Simple (XML::SAX::Expat):
   7 wallclock secs ( 6.50 usr +  0.03 sys =  6.53 CPU) @ 30.62/s (n=200)

As the benchmark shows, all SOAP::WSDL parser variants are faster than 
XML::Simple with XML::SAX::Expat, and SOAP::WSDL::Expat::MessageParser almost 
reaches the performance of XML::Simple with XML::Parser as backend.

Parsing SOAP responses in chunks does not increase speed - at least not up 
to a response size of around 500k:

 Benchmark: timing 5 iterations of SOAP::WSDL::SAX::MessageHandler, 
   SOAP::WSDL::Expat::MessageParser, SOAP::WSDL::Expat::MessageStreamParser...
 
 SOAP::WSDL::Expat::MessageStreamParser: 
 13 wallclock secs ( 7.39 usr +  0.09 sys =  7.48 CPU) @  0.67/s (n=5)

 SOAP::WSDL::Expat::MessageParser: 
 10 wallclock secs ( 5.81 usr +  0.06 sys =  5.88 CPU) @  0.85/s (n=5)
 
 SOAP::WSDL::SAX::MessageHandler: 
 14 wallclock secs ( 8.78 usr +  0.03 sys =  8.81 CPU) @  0.57/s (n=5)
 
 Response size: 344330 bytes

=head1 OLD SAX HANDLER

The old SAX handler historically used in SOAP::WSDL are not included in 
the SOAP::WSDL package any more.

However, they may be obtained from the "attic" directory in 
SOAP::WSDL's SVN repository at 

https://soap-wsdl.svn.sourceforge.net/svnroot/soap-wsdl/SOAP-WSDL/trunk/attic

=over

=item * SOAP::WSDL::SAX::WSDLHandler

This is a SAX handler for parsing WSDL files into object trees SOAP::WSDL 
works with.

It's built as a native handler for XML::LibXML, but will also work with 
XML::SAX::ParserFactory.

To parse a WSDL file, use one of the following variants:

 my $parser = XML::LibXML->new();
 my $handler = SOAP::WSDL::SAX::WSDLHandler->new();
 $parser->set_handler( $handler );
 $parser->parse( $xml );
 my $data = $handler->get_data();
 
 
 my $handler = SOAP::WSDL::SAX::WSDLHandler->new({
        base => 'XML::SAX::Base'
 });
 my $parser = XML::SAX::ParserFactor->parser(
    Handler => $handler
 );
 $parser->parse( $xml );
 my $data = $handler->get_data();


=item * SOAP::WSDL::SAX::MessageHandler

This is a SAX handler for parsing WSDL files into object trees SOAP::WSDL 
works with.

It's built as a native handler for XML::LibXML, but will also work with 
XML::SAX::ParserFactory.

Can be used for parsing both streams (chunks) and documents. 

=back


=cut