MidLifeXis has asked for the wisdom of the Perl Monks concerning the following question:

I am having a hard time with sending SOAP / .NET "complex" structures. If I send the following response, my .NET (well, .NET WebServiceStudio) does not recognize the response -- it comes back with a "null" as the value of the string.

<?xml version="1.0" encoding="utf-16"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" x +mlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="h +ttp://www.w3.org/2001/XMLSchema" soap:encodingStyle="http://schemas.x +mlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/soa +p/envelope/"> <soap:Body> <Test1Response xmlns="urn:TESTServices"> <OutData xsi:type="typens:MyStringType" xmlns:typens="urn:TESTSe +rvices"> <MyStringType xsi:type="xsd:string">Hi There</MyStringType> </OutData> </Test1Response> </soap:Body> </soap:Envelope>

If I hardcode the return string to the above, I get the same response. However, if I change the attributes on the "Test1Response" tag above to be <namesp1:Test1Response xmlns:namesp1="urn:TESTServices">, all works fine.

<?xml version="1.0" encoding="utf-16"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" x +mlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="h +ttp://www.w3.org/2001/XMLSchema" soap:encodingStyle="http://schemas.x +mlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/soa +p/envelope/"> <soap:Body> <namesp1:Test1Response xmlns:namesp1="urn:TESTServices"> <OutData xsi:type="typens:MyStringType" xmlns:typens="urn:TESTSe +rvices"> <MyStringType xsi:type="xsd:string">Hi There</MyStringType> </OutData> </namesp1:Test1Response> </soap:Body> </soap:Envelope>

My function in my handler is:

sub Test1 { my $self = shift; my $envelope = pop; my $indata = $envelope->valueof('/Envelope/Body/*/InData'); my $d= ( SOAP::Data ->name( 'OutData' => \SOAP::Data ->value( SOAP::Data->name('MyStringType' => 'Hi There') ) ) ->attr({ 'xmlns:typens' => 'urn:TESTServices', 'xsi:type' => 'typens:MyStringType', }, ) ); return $d; }

Does anyone have a good example of how to add the explicit namespace tag to the service name (the first child of the Body tag) using SOAP::Lite? I am not specifying the tag, I think it is supplied by SOAP::Lite.

Thanks,

update: Missed name space on closing tag.

--MidLifeXis

Replies are listed 'Best First'.
Re: SOAP::Lite, .NET, and complex structures
by szbalint (Friar) on Aug 09, 2007 at 06:32 UTC
    I've spent a good deal of time not too long ago to try to work around problems like this. I've finally had given up on trying to use SOAP for two reasons: SOAP::Lite is quite complex and dated, it might be hard to use and hard to debug. This coupled with the type agnosticism of Perl makes for a suboptimal SOAP client/server (can't easily autogenerate WSDL).

    I've even written my own WSDL definition file, read the specifications for both SOAP and WSDL, but basically what that experience has taught me is that either you're willing to learn these specifications in detail, and you have willingness to implement a SOAP module on top of / instead of SOAP::Lite, you're going to run into issues like the ones you're experiencing.

    Now, your problem can be worked around, but that's by getting into SOAP::Lite internals (the module is a bit weird to make an understatement, so internals is a weird word for this as the module doesn't really have a clear boundary between internal/external methods), possibly using SOAP::SOM and SOAP::Data (they are both part of the SOAP module on CPAN).

    Documentation is a scarce thing, apart from the SOAP and WSDL specifications you can find some information on http://www.soaplite.com/. The archives contain quite a lot of solutions for issues similar to these, but sadly they appear to be the only hint on how to solve more complex problems. Also, you might find the cookbook helpful, or the guide, but they are both unfinished.

    Avoid SOAP and SOAP::Lite if you can (like I did), as it's very very hard to make it work in case you're interoperating with different, non SOAP::Lite clients. If you can't, you need to read the specifications, and possibly after going through the scarce documentation available, after digging through the sometimes cryptic SOAP::Lite source at places, then you can possibly make it work.

      Unfortunately, I am not able to get rid of SOAP (different department is talking to our data) at this time, so I do need to work around it.

      What I have done, following the link to the cookbook above, is added this to by dispatcher code:

      BEGIN { package MySerializer; @MySerializer::ISA = 'SOAP::Serializer'; sub envelope { $_[2] = (SOAP::Data ->name($_[2]) ->prefix("TESTAPI") ->uri("urn:TESTServices") ) if $_[1] =~ /^(?:method|response)$/; shift->SUPER::envelope(@_); } }

      This seemed to get me through the part that was stopping me. I now, however, am getting an error of System.Xml.XmlException: There is invalid data at the root level. Line 1, position 1. from the .NET WebService Studio client. Looking for what causes that now.

      --MidLifeXis

Re: SOAP::Lite, .NET, and complex structures
by erroneousBollock (Curate) on Aug 10, 2007 at 05:06 UTC
    As is my tradition in these parts, I recommend SOAP::WSDL because it solves the following problems:
    • WSDL-based auto-generation of proxy code
    • very flexible document encoding detection (talks to .NET out of the box)
    • automatic marshalling between perl data-structures and SOAP complex types (those described in the WSDL in any case)... manual use of SOAP::Data is tiresome
    • data-validation comes for free
    • it's a subclass of SOAP::Lite's client, so the normal APIs are also available

    At most, you'll have to change the contents of the default on_action handler to reflect your server's SOAPAction expectations (it works straight away for .NET based services).

    -David

      Thanks, but isn't SOAP::WSDL a client library? Perl is running the service, and an MS client is reading the data as a .NET service.

      If I am wrong, I would be happy to learn more about it, but my reading of the documents indicates that SOAP::WSDL is only a client.

      --MidLifeXis

        Ok, I didn't understand it that way from your description.

        The .NET client is likely doing all the right things wrt SOAP, so let's look at the perl web-service (assuming you used the standard tools to generate your proxy class).

        Firstly, are you publishing WSDL for your service? If so, how did you construct it?
        If your WSDL is not exactly correct, you'll get all sorts of namespace/type/arity issues.

        I use Pod::WSDL to generate WSDL from my web-service classes. It requires you to add some POD to each method and class because perl doesn't handle types/arity statically. In practice, this just makes your class better documented.

        WSDL descriptions of each method can generally be a little loose because perl (and SOAP::Lite) doesn't really molest the data too much before passing to the method... however, it's really important to properly describe (annotate) your classes (complex types).

        I actually have my web-services serve up the WSDL at http://server/path/to/service?WSDL

        Microsoft's wsdl.exe proxy class generator tool should have no problem consuming WSDL generated by Pod::WSDL.

        -David

Re: SOAP::Lite, .NET, and complex structures
by ForgotPasswordAgain (Vicar) on Aug 09, 2007 at 07:40 UTC