[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