NAME

SOAP::WSDL::SAX::MessageHandler - Convert SOAP messages to custom object trees

SYNOPSIS

# this is the direct variant, recommended for performance
use SOAP::WSDL::SAX::MessageHandler;
use XML::LibXML;

my $filter = SOAP::WSDL::SAX::MessageHandler->new( {
   class_resolver => FakeResolver->new()
), "Object creation");
my $parser = XML::LibXML->new();
$parser->set_handler( $filter );

$parser->parse_string( $soap_message );

my $object_tree = $filter->get_data();


# This is the XML::ParserFactory variant - for those who want other
# parsers than XML::Simple....
use SOAP::WSDL::SAX::MessageHandler;
use XML::SAX::ParserFactory;

my $filter = SOAP::WSDL::SAX::MessageHandler->new( {
   class_resolver => FakeResolver->new(),
   base => 'XML::SAX::Base',
), "Object creation");
my $parser = XML::LibXML->new();
$parser->set_handler( $filter );

$parser->parse_string( $soap_message );

my $object_tree = $filter->get_data();

DESCRIPTION

Parses a SOAP message into an object tree.

For every element in the SOAP message, an object is created. The class of the object is determined via a Resolver object which has to be passed to new via the class_resolver parameter.

Writing a class resolver

The class resolver must returned a 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 FakeResolver;

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 {}, 'FakeResolver' };

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

Writing type library classes

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

Type library classes must provide the following methods:

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

  • new

    Constructor

  • add_FOO

    The add_FOO method is called for every child element of the XML node.

    Characters are regarded as child element of the last XML node.

A tyelib class implemented as Inside-Out object using Class::Std::Storable as base class would look like this:

package Typelib::TEnqueueMessage;
use strict;
use Class::Std::Storable;

my %MMessage_of :ATTR(:name<MMessage> :default<()>);

sub add_MMessage {
       my ($self, $value) = @_;
       my $ident = ident $self;

       # we're the first value
       return $MMessage_of{ $ident } = $value
            if not defined $MMessage_of{ $ident };

       # we're the second value
       return $MMessage_of{ $ident } = [
            $MMessage_of{ $ident }, $value ]
                if not ref $MMessage_of{ $ident } eq 'ARRAY';

       # we're third or later
       push @{ $MMessage_of{ $ident } }, $value;
       return $MMessage_of{ $ident };
   }
}
1;

Of course one could use a method factory for these add_FOO methods - see t/lib/Typelib/Base.pm for an example.

Performance

SOAP::WSDL::SAX::MessageHandler with a raw XML::LibXML parser almost reaches the performance of XML::Simple with XML::Parser (and expat) as low-level parser.

And SOAP::WSDL::SAX::MessageHandler builds up a object tree, while XML::Simple just emits hash data structures:

SOAP::WSDL::SAX::MessageHandler:
   1 wallclock secs ( 1.39 usr +  0.00 sys =  1.39 CPU) @ 719.42/s (n=1000)

XML::Simple:
   2 wallclock secs ( 1.25 usr +  0.01 sys =  1.26 CPU) @ 790.51/s (n=1000)

If you know a faster way for parsing XML with a reasonable simple API than XML::LibXML, please let me know...

Bugs and Limitations

  • Ignores all namespaces

  • Does not handle mixed content

  • The SOAP header is ignored

AUTHOR

Replace the whitespace by @ for E-Mail Address.

Martin Kutter E<lt>martin.kutter fen-net.deE<gt>

COPYING

This module may be used under the same terms as perl itself.

Repository information

$ID: $

$LastChangedDate: $
$LastChangedRevision: $
$LastChangedBy: $

$HeadURL: $