[Xml-compile] Name spaces and node types

Patrick Powell papowell at astart.com
Thu Sep 19 17:47:30 GMT 2013


I am dealing with a XML schema that has several name spaces and the same 
element name
in each namespace (Yes, yes, I know this is bad practice).

In the XML::SOAP::Server.pm,  version 2.38,  you have the 
compileFilter() routine:

sub compileFilter(@)
{   my ($self, %args) = @_;
     my $nodetype;
     if(my $first    = $args{body}{parts}[0])
     {   $nodetype = $first->{element}
#           or panic "cannot handle type parameter in server filter";
             || $args{body}{procedure};  # rpc-literal "type"
     }

     # called with (XML, INFO)
       defined $nodetype
     ? sub { my $f =  $_[1]->{body}[0]; defined $f && $f eq $nodetype }
     : sub { !defined $_[1]->{body}[0] };  # empty body
}

During generation of the Server code,  the compileFilter routine is 
called with
$nodetype = '{http://www.specsol.com/rmisatmsdata}errorReportMsg'

Notice that this has the namespace prefix.  It is used to generate a 
CODE object:

sub { my $f =  $_[1]->{body}[0]; defined $f && $f eq $nodetype }

When you have a SoapAction header,  then the code in 
XML::SOAP::Daemon.pm, the
process() routine ends doing:

     # Try to resolve operation via soapAction
     my $sa = $self->{sa_input_rev};
     if(defined $soapaction)
     {   if(my $name = $sa->{$soapaction})
         {   my $handler = $handlers->{$name};
             local $info->{selected_by} = 'soap-action';
             my ($rc, $msg, $xmlout) = $handler->($name, $xmlin, $info, 
$req);  <<<<<  XXX
             if($xmlout)
             {   trace "data ready for $version $name, via sa 
'$soapaction'";
                 return ($rc, $msg, $xmlout);
             }
         }
     }
The $name value is 'GetFaultRequest', $xmlin is the parsed XML( an 
XML::LibXML::Element object),
and $info is a hash with:
    'body' => ARRAY(0x82d7d330)
       0  'errorReportMsg'<<<<<<< this is the element name - notice no 
namespace
    'header' => ARRAY(0x82d7d300)
         empty array
    'selected_by' => 'soap-action'
    'soap_version' => 'SOAP11'
    'wsa_action' => undef

The $handler is a CODE object generated by the 
XML::Compile::SOAP::Server.pm compileHandler() routine.
See about line 49 in XML/Compile/SOAP/Server.pm, where you have:

     sub
     {   my ($name, $xmlin, $info, $session) = @_;
         # info is used to help determine if the xmlin is of the type for
         # this call. $session is passed in by the server and is in turn
         # passed to the handlers
         $selector->($xmlin, $info) or return;
         trace __x"procedure {name} selected", name => $name;


Here we have the $name,$xmlin, $info as before.  We call the $selector, 
which is the code generated by compileFilter,  which is what we started 
this discussion with:

      sub { my $f =  $_[1]->{body}[0]; defined $f && $f eq $nodetype }

$_[0] is the $xmlin value
$_[1] is the $info value
    $_[1]{body}[0] is 'errorReportMsg'
$nodetype is the value passed to compileFilter -
        $nodetype = '{http://www.specsol.com/rmisatmsdata}errorReportMsg'


Now in the routine $f = 'errorReportMsg', $nodetype = 
'{http://www.specsol.com/rmisatmsdata}errorReportMsg'
Comparison: $f eq $nodetype

The comparison fails, the call to $selector->($xmlin, $info) returns a 
'tests False' value,  and the code trying to determine that handler 
moves on to the next method, which is to use the object type to 
determine a match against the input message element.

It turns out that the same test (sub { my $f = $_[1]->{body}[0]; defined 
$f && $f eq $nodetype }) and values for the $nodetype and $body are 
used,  and the test fails.  You get the Fault message:

       <faultstring>SOAP11 body element errorReportMsg (soapAction 
GetFaultRequest) not recognized, available ports are 
GenerateUpdateDataRequest GetDataRequest GetFaultRequest GetMetaRequest 
GetOPLDSRequest GetStatusRequest OPLDSMsg UpdateDataRequest</faultstring>

Solution to the problem  Version 1 (Brutal and Incorrect):
Replace:
sub { my $f =  $_[1]->{body}[0]; defined $f && $f eq $nodetype }
with
sub { my $f =  $_[1]->{body}[0];
  my $nons = $nodetype; $nons =~ s/.*\}//;  <<< I think there is a 
utility function to do this
  defined $f && ($f eq $nodetype or $f eq $nons) }

Solution to the problem Version 2 (More correct,  but I have no idea how 
to do this):

The problem seems to be with the $info information.   The {body} value 
does not have a namespace.
Perhaps another hash entry can be added, say
{ns => ['{http://www.specsol.com/rmisatmsdata}errorReportMsg'] }.

This could be done during the xmlin parsing.  Now you can do:

my $f =  $_[1]->{body}[0];
my $ns = $_[1]->{ns}[0];   <<< body element with namespace?
defined $f && ($f eq $nodetype or $ns eq $nodetype)


I have a funny feeling that this may not be the only place where is 
problem occurs.



More information about the Xml-compile mailing list