I am trying to communicate to a webservice using Suds, reading from the service works fine, however writing throws an error.
suds.WebFault: Server raised fault: ‘The formatter threw an exception
while trying to deserialize the message: There was an error while
trying to deserialize parameter http://tempuri.org/:tagValues. The
InnerException message was ‘Element Value from namespace
http://schemas.datacontract.org/2004/07/NOV.Api.Messages cannot have
child contents to be deserialized as an object. Please use XmlNode[]
to deserialize this pattern of XML.’. Please see InnerException for
more details.’
The XML produces does not seem to add the neccessary xsi:type=”xsd:int”
Produced:
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value>23</ns1:Value>
</ns1:TagValue>
Expected:
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value xsi:type="xsd:int">23</ns1:Value>
</ns1:TagValue>
After searching around i figured to try the ImportDoctor to see if i could get in the xsi:type
I added
schema_url = 'http://schemas.xmlsoap.org/soap/encoding/'
schema_import = Import(schema_url)
schema_doctor = ImportDoctor(schema_import)
and doctor=schema_doctor in the Client ctor
This now gave me an additional prefix and a much extended list of Types
Prefixes (4)
ns0 = "http://schemas.datacontract.org/2004/07/NOV.Api.Messages"
ns1 = "http://schemas.microsoft.com/2003/10/Serialization/"
ns2 = "http://schemas.xmlsoap.org/soap/encoding/"
ns3 = "http://tempuri.org/"
I now have a ns2:int
I used the factory to create an object of type ns2:int, setting its value to 23
When sending this, i get the following XML:
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value xsi:type="ns2:int">23</ns1:Value>
</ns1:TagValue>
I now get the following exception when trying to send it:
suds.WebFault: Server raised fault: ‘The formatter threw an exception
while trying to deserialize the message: There was an error while
trying to deserialize parameter http://tempuri.org/:tagValues. The
InnerException message was ‘Error in line 1 position 651. Element
‘http://schemas.datacontract.org/2004/07/NOV.Api.Messages:Value’
contains data from a type that maps to the name ‘http://schemas.xm
lsoap.org/soap/encoding/:int’. The deserializer has no knowledge of
any type that maps to this name. Consider using a DataContractResolver
or add the type corresponding to ‘int’ to the list of known types –
for example, by using the KnownTypeAttribute attribute or by adding it
to the list of known types passed to DataContractSerializer.’. Please
see InnerException for more details.’
Seems slightly closer, but seems like there is some mess with namespaces?
Full XML produced:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns3="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns0="http://tempuri.org/" xmlns:ns1="http://schemas.datacontract.org/2004/07/NOV.Api.Messages" xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns3:Body>
<ns0:WriteRealtimeValues>
<ns0:tagValues>
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value xsi:type="ns2:int">23</ns1:Value>
</ns1:TagValue>
</ns0:tagValues>
</ns0:WriteRealtimeValues>
</ns3:Body>
</SOAP-ENV:Envelope>
As reference, I create the client using the following code
credentials = dict(username='%s' % (username), password='%s' % password)
url= "http://%s:%s/TagValueWriteService?wsdl" % (ip,port)
self.transport = HttpAuthenticated(**credentials)
suds.client.Client.__init__(self,url, transport=self.transport, cache=None,doctor=schema_doctor)
There seem to be several similar issues here on stackoverflow, most of them mentioning the ImportDoctor in a similar manner as i tried. I am lacking some of the fundamental understanding of SOAP i suspect…
I managed to solve it, using the answer from Adding xsi:type and envelope namespace when using SUDS ( https://stackoverflow.com/a/10977734/696768 )
I am not sure this is the only possible solution, and to me it seems more of a hack than anything else, however it will work fine for my current scenario.
The solution i used, is making a plugin for the client, looking for the particular element that i need to be xsi:type=”xsd:int”, then adding these attributes to those elements.
The code i ended up using for reference (from the aforementioned stackoverflow question with minor adjustments):
Then I added plugins=[plugin] to the client ctor.
Example: