Re: Java/Oracle server, PHP 5.1.2 client, array of complexTypes, Deserializer error

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This post is to document my particular workaround to this interoperability
issue.

To sum up the problem: PHP follows the SOAP 1.1 guidelines in defining array
types when it defines them as being a type derived from "SOAP-ENC:Array".
However, servers written in other languages (Java on Oracle in my case) have
problems interpreting the PHP-generated arrays because they have no
knowledge of the derived types, since those types are defined in the WSDL,
not in the server code.  This problem affects arrays in general, not just
arrays of complex types.  It is my oh-so-humble (and rather naive too, I'm
sure) opinion that PHP should NOT use derived types, but there are probably
edge cases where that is necessary, and that's the reason PHP does it that
way.  Makes it rather difficult when no other SOAP server implementation
handles derived types.  OK, OK, so I don't know for sure that NO other
implementation handles derived types, I just mean that Java, .NET, and C++
implementations don't.  Keep the flames low.... :-)

Because I am also writing my own server, my workaround was to implement a
pre-filter that looks for instances of derived array types and replaces them
with "SOAP-ENC:Array".  This actually works rather well, although it's not a
"real" solution.

For those who do not have access to the server, the most prevalent
workaround I've seen is to hand-build the request and send it using
SoapClient->__doRequest().  The problem with that approach is that it's
tedious and fragile: if the specs change, you need to change your code.  As
an alternative, perhaps the following will work:

- Construct the request normally.
- Send the request, trapping for errors.
- If you get an error indicating that the server did not recognize the array
type (in my case, it's a deserialization error), do the following:
- Extract the request into a variable using Soapclient->__getLastRequest().
- Replace every instance of xsi:type="<your namespace>:ArrayOf<array
contents>" with xsi:type="<SOAP-ENC namespace>:Array" (you'll probably have
to find and use the local name of the SOAP-ENC namespace, rather than
assuming it's going to be "SOAP-ENC").
- Send another request using SoapClient->__doRequest(), passing it the
now-massaged previous request.  This requires you to know the endpoint url
and SOAP action, information you can get from the WSDL.  Or perhaps
SoapClient->__getLastRequestHeaders() can help here...

The really strong point of this approach is that it enables you to ignore
the rest of the underlying SOAP structure, allowing your code to more easily
handle changing specifications.
The obvious problem with this approach is the lag time introduced by the
initial unsuccessful request.  If a significant number of your requests
involve arrays, perhaps you can instantiate your SoapClient object with
localhost (or some other bogus hostname) for the location, allow it to make
the initial request, which should be very fast now, since it will simply
return host not found, or a similar error, then do the massaging and
re-sending.  I don't know of any way to get the request content before
sending.

Well, that's where I am at this point.

I'd appreciate any other suggestions/insight/corrections.

Thanks,

-Peter


""Peter Guy"" <pguy@vrcis.com> wrote in message
31.A0.13436.07F3DC34@pb1.pair.com">news:31.A0.13436.07F3DC34@pb1.pair.com...
> I posted this last night, but it hasn't shown up, so I'm posting it
again...
> My apologies if this ends up being a double post.
>
> This seems to be a recurring problem, but I have yet to find an answer.
>
> I have implemented a web service written in Java, hosted on an Oracle
> Application Server (version 9.0.4).
> The web service makes heavy use of custom objects, written as Java Beans
on
> the server-side, and implemented as complex types in the WSDL.
>
> Using PHP 5.1.2 with its built-in SOAP implementation, I am able to
consume
> the WSDL w/out errors and make most calls using PHP classes to emulate the
> WSDL's complex types.  The problem comes in when a transaction requires an
> array of one of the complex types.
> Java-based clients have no problems; neither do .NET-based ones.
>
> I built a simple example that illustrates the problem; let's get right
into
> it.  This is a live example, so you can do some testing on your own if
you'd
> like.
>
> The service is here:
> http://vrcweb1.vrcis.com/custserv/CustomerService
> The WSDL can be found here:
> http://vrcweb1.vrcis.com/custserv/CustomerService?WSDL
>
> There is one operation: EchoBars.  It accepts an array of Bar objects,
then
> returns that array.
> The Bar object has one field: NAME, of type string.
>
> This is the PHP code I use to invoke EchoBars:
>
> $WSDL = "http://vrcweb1.vrcis.com/custserv/CustomerService?WSDL";;
> $client = new SoapClient($WSDL);
> $b1 = new Bar();
> $b1->NAME = "bar none";
> try{$client->EchoBars(array(1=>$b1));}catch(Exception $Ex){echo $Ex;}
>
> This is the request it generates:
>
> <?xml version="1.0" encoding="UTF-8"?>
> <SOAP-ENV:Envelope
>   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/";
>   xmlns:ns1="CustomerService"
>   xmlns:ns2="http://www.vrcis.com/CustomerServiceAPIV1";
>   xmlns:xsd="http://www.w3.org/2001/XMLSchema";
>   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
>   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/";
>   SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/";>
>   <SOAP-ENV:Body>
>     <ns1:EchoBars>
>       <BARS
>         SOAP-ENC:arrayType="ns2:Bar[1]"
>         xsi:type="ns2:ArrayOfBar">
>         <item xsi:type="ns2:Bar">
>           <NAME xsi:type="xsd:string">bar none</NAME>
>         </item>
>       </BARS>
>     </ns1:EchoBars>
>   </SOAP-ENV:Body>
> </SOAP-ENV:Envelope>
>
> Which request returns this error message:
>
> No Deserializer found to deserialize a
> http://www.vrcis.com/CustomerServiceAPIV1:ArrayOfBar using encoding style
> http://schemas.xmlsoap.org/soap/encoding/.
>
>
> Here is a request that works:
>
> <?xml version='1.0' encoding='UTF-8'?>
> <SOAP-ENV:Envelope
>   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/";
>   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
>   xmlns:xsd="http://www.w3.org/2001/XMLSchema";>
>   <SOAP-ENV:Body>
>     <ns1:EchoBars
>       xmlns:ns1="CustomerService"
>       SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/";>
>       <BARS
>         xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/";
>         xsi:type="ns2:Array"
>         xmlns:ns3="http://www.vrcis.com/CustomerServiceAPIV1";
>         ns2:arrayType="ns3:Bar[1]">
>         <item xsi:type="ns3:Bar">
>           <NAME xsi:type="xsd:string">bar none</NAME>
>         </item>
>       </BARS>
>     </ns1:EchoBars>
>   </SOAP-ENV:Body>
> </SOAP-ENV:Envelope>
>
> Notice that the only real difference between the two requests is the
"type"
> of the BARS element.  In the PHP-generated one it is "<my
> namespace>:ArrayOfBar", while it is "<SOAP-ENC namespace>:Array" in the
> other one.
> There are other differences, but they are all naming conventions and don't
> apply.
>
> To generate another request that works, goto
> http://www.soapclient.com/soaptest.html.  It'll generate a request that is
> substantively the same as the one above.
>
> I think I know what's happening, I just don't know why, or if there's a
way
> to make it "happen" correctly.  Or some way to reformat my WSDL so that
PHP
> interprets it correctly.
>
> The way I understand it, in the WSDL, arrays of complex types are defined
> with a level of indirection:
>
> <complexType name="Bar">
> </complexType>
> <complexType name="ArrayOfBar">
>   <attribute arrayType="Bar[]"/>
> </complexType>
>
> It seems to work kinda like pointers in C: ArrayOfBar is a reference to an
> array of Bar objects.
>
> What appears to be happening is that PHP is not following that reference,
> but is instead grabbing the value of the variable rather than
dereferencing
> it and using _that_ value (to use pointer lingo).
>
>
> Naturally, the server replies (to the PHP-generated request), "I have no
> idea what an 'ArrayOfBar'-type object is".
>
> So, my questions:
>
> 1. Is the WSDL formatted the right way for PHP?  Could it be formatted
> differently, so as to coerce PHP into supplying the correct "type" for the
> BARS element?
> 2. Is there any option or flag in PHP's SOAP implementation that will set
up
> PHP to read the WSDL differently?
> 3. Is there a better way (other than an array) to implement a parameter
that
> is a collection of complexTypes?
> 3. Does anyone know what the heck I'm talking about? ;-)
>
> Thanks,
>
> -Peter

-- 
PHP Soap Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [PHP Users]     [Kernel Newbies]     [PHP Database]     [Yosemite]

  Powered by Linux