I have a WSDL that has an element that requires an attribute:
<xsd:complexType name="claim"> <xsd:annotation> <xsd:documentation>Claim Element</xsd:documentation> </xsd:annotation> <xsd:sequence> <!-- other elements removed --> </xsd:sequence> <xsd:attribute name="claimId" type="xsd:nonNegativeInteger" use="required" /> </xsd:complexType>
In terms of generated xml, it should look like:
<claims> <claim claimId="1"> <!-- elements removed --> </claim> <!-- more claims --> </claims>
Within a foreach
loop I am putting together an array of elements and using the attribute as part of the key:
//$claim = array of key/value pairs $claim = [...]; $claim = new \SoapVar($claim, SOAP_ENC_OBJECT, null, null, 'claim claimId="' . ($key+1) . '"'); $claims['claim claimId="'.($key+1).'"'] = $claim;
When it comes to passing this to the SoapClient, the elements get removed:
//$client = new \SoapClient($wsdl); $client->checkClaims($claims);
But all I'm getting is:
<claims />
How do I get my soap client to parse the claim
elements correctly in the soap call?
2 Answers
Answers 1
So there are few issues with your code. For this to work, you need to use the SoapClient in WSDL mode ($client = new \SoapClient($wsdl);
, which you are doing). Next below is wrong
$claim = [...]; $claim = new \SoapVar($claim, SOAP_ENC_OBJECT, null, null, 'claim claimId="' . ($key+1) . '"'); $claims['claim claimId="'.($key+1).'"'] = $claim;
You don't add attributes using 'claim claimId="' . ($key+1) . '"'
.
Now what you need is to use a classmap
. Below is a sample python flask app I created to show the WSDL
from flask import Flask app = Flask(__name__) @app.route("/ICalculator",methods=['get', 'post']) def reply(): return "<xmldata />" @app.route("/app.wsdl") def send(): return """<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" targetNamespace="http://localhost:5001" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <wsdl:types> <xsd:schema targetNamespace="http://localhost:5001" elementFormDefault="qualified" > <xsd:element name="Claim"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" name="a" type="xsd:int" /> <xsd:element minOccurs="0" name="b" type="xsd:int" /> </xsd:sequence> <xsd:attribute name="claimId" type="xsd:nonNegativeInteger" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="AddResponse"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" name="result" type="xsd:int" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </wsdl:types> <wsdl:message name="ICalculator_Add_InputMessage"> <wsdl:part name="parameters" element="tns:Add" /> </wsdl:message> <wsdl:message name="ICalculator_Add_OutputMessage"> <wsdl:part name="parameters" element="tns:AddResponse" /> </wsdl:message> <wsdl:portType name="ICalculator"> <wsdl:operation name="Add"> <wsdl:input wsaw:Action="http://localhost:5001/ICalculator/Add" message="tns:ICalculator_Add_InputMessage" /> <wsdl:output wsaw:Action="http://localhost:5001/ICalculator/AddResponse" message="tns:ICalculator_Add_OutputMessage" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="DefaultBinding_ICalculator" type="tns:ICalculator"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="Add"> <soap:operation soapAction="http://localhost:5001/ICalculator/Add" style="document" /> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="CalculatorService"> <wsdl:port name="ICalculator" binding="tns:DefaultBinding_ICalculator"> <soap:address location="http://localhost:5001/ICalculator" /></wsdl:port> </wsdl:service> </wsdl:definitions> """.replace(r"\r", "").replace(r"\n", "") if __name__ == "__main__": app.run(debug=True, host='0.0.0.0', port=5000)
Then ran the same using
python3 wsdl.py
And ran another socat
to view the traffic
socat -v TCP-LISTEN:5001,fork TCP:127.0.0.1:5000
Next I wrote a sample PHP code to show how classmap
works
<?php class Claim { public function __construct(Array $properties=array()){ foreach($properties as $key => $value){ $this->{$key} = $value; } } } $test = new Claim(array('claimId'=>10, 'a'=> 22, 'b'=> 33)); $claim=new SoapVar($test, SOAP_ENC_OBJECT); $wsdl = "http://localhost:5001/app.wsdl"; $client = new SoapClient($wsdl, array( 'trace' => 1, 'encoding' => 'UTF-8', 'soap_version' => SOAP_1_1, 'classmap' => array('Claim' => 'Claim') )); $client->add($claim);
And the resultant xml
is
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://localhost:5001" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Body><parameters claimId="10" xsi:type="ns1:Claim"><ns1:a>22</ns1:a><ns1:b>33</ns1:b></parameters></SOAP-ENV:Body></SOAP-ENV:Envelope>
References:
PHP soap request with an element attribute and child elements
Adding attributes to the actual function tag in PHP soapCall
How do I add additional attributes to XML Elements with the SoapClient Class in PHP
php SoapVar not setting attributes
Getting the XML as string for a SoapVar variable - without a webservice (locally)?
Getting the XML as string for a SoapVar variable - without a webservice (locally)?
http://fvue.nl/wiki/Php:_Soap:_How_to_add_attribute_to_SoapVar
https://forums.phpfreaks.com/topic/137357-solved-php-soap-client-node-attributes/
http://eosrei.net/articles/2012/01/php-soap-xml-attributes-namespaces-xmlwriter
Answers 2
Knowing that \SoapClient
can accept an xml string, I decided to convert my array of data into an xml string:
/** * Convert an array to an xml. Recursive function. * @param array $array * @param String $rootElement OPTIONAL name of root element. * @param \Simple XMLElement $xml OPTIONAL * @return String */ function arrayToXml($array, $rootElement = null, $xml = null) { $_xml = $xml; if ($_xml === null) { $_xml = new \SimpleXMLElement($rootElement !== null ? $rootElement : '<root/>'); } foreach ($array as $k => $v) { if (is_array($v)) { //nested array arrayToXml($v, $k, $_xml->addChild($k)); } else { $_xml->addChild($k, $v); } } //Remove xml doctype and root name spaces. return str_replace(["<?xml version=\"1.0\"?>\n", '<root>', "</root>\n"], '', $_xml->asXML()); } //my array of claims $claims = [ 'claim claimId="1"' => [ //... ], 'claim claimId="2"' => [ //... ], //... ]; $claims = arrayToXml($claims);
This generates an xml string of:
<claim claimId="1"><!-- stuff --></claim claimId="1"><claim claimId="2"><!-- stuff --></claim claimId="2">
Next step is to remove the attribute from the closing tags:
$claims = preg_replace('#</claim claimId="\d+">#', '</claim>', $claims);
Finally, I wrap my xml in the appropriate parameter name and convert it to a \SoapVar
so the \SoapClient
will parse it correctly:
$claims = '<ns1:claims>' . $claims . '</ns1:claims>'; $claims = new \SoapVar($claims, XSD_ANYXML, 'http://www.w3.org/2001/XMLSchema-instance');
Passing $claims
into my \SoapClient
and running var_dump($client->__getLastRequest());
shows my generated xml as:
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Body><ns1:claims><claim claimId="1"><!--stuff--></claim><claim claimId="2"><!--stuff--></claim></ns1:claims></SOAP-ENV:Body></SOAP-ENV:Envelope>
Just as it wanted.
0 comments:
Post a Comment