I am trying to use suds to make a request to a remote SOAP service. I need to set the namespace on the parameters thus:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://myemployer.com/" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<AuthHeader xmlns="http://myemployer.com/">
<Token>blah</Token>
</AuthHeader>
</SOAP-ENV:Header>
<ns1:Body>
<ns0:AwardBadgesBatch>
<ns0:badgeAwards>
<AwardBadge>
<EnterpriseID>jhoov11</EnterpriseID>
...
</AwardBadge>
</ns0:badgeAwards>
<ns0:includeSuccessStatus>false</ns0:includeSuccessStatus>
</ns0:AwardBadgesBatch>
</ns1:Body>
</SOAP-ENV:Envelope>
Note the 'ns0' on both the badgeAwards and includeSuccessStatus elements. I also need to not have prefixes on the stuff inside badgeAwards.
I am using this code to make the request:
from suds.client import Client
from suds.sax.element import Element
from suds.sax.attribute import Attribute
from suds.plugin import MessagePlugin
import logging
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
logging.basicConfig(level=logging.DEBUG)
url='https://myemployer.com/BadgingServices.asmx?wsdl'
c = Client(url)
t = c.service.Authenticate('blah')
t = t.Authenticate.Status.Token
header_root = Element('AuthHeader')
header_token = Element('Token').setText(t)
header_attribute = Attribute('xmlns', "http://myemployer.com/")
soap_header = header_root.insert(header_token)
soap_header.append(header_attribute)
class NamespaceFixer(MessagePlugin):
''' must add namespace prefix to all parameters '''
def marshalled(self, context):
body = context.envelope[1]
for i in body[0]:
i.setPrefix('ns0')
print "******* element after setting prefix: %s ********" % i.plain()
nsf = NamespaceFixer()
c.set_options(soapheaders=soap_header, plugins=[nsf])
from suds.sax.element import Element
ba2 = Element('badgeAwards')
ba = Element('AwardBadge')
ba2.append(ba)
entid = Element('EnterpriseID').setText('jhoov11')
ba.append(entid)
...
includeSuccess = Element('includeSuccessStatus').setText('false')
c.service.AwardBadgesBatch(badgeAwards= ba2, includeSuccessStatus=includeSuccess)
Now when I do this, I see the xml text output before the marshall() call, and I see the output from that call:
******* element after setting prefix: <ns0:badgeAwards/> ********
******* element after setting prefix: <ns0:includeSuccessStatus/> ********
but the folks on the other end insist (insist!) that the prefix is not set on those elements when they receive it.
Two questions come to mind:
am I doing something wrong in my MessagePlugin?
is there a way to display the final modified XML text before it gets sent off? I think I'm setting it but there's only so much I can say to them when I can't see the final full text I'm sending.
EDIT:
c.last_sent().plain()
u'<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:ns0="http://pebble.searshc.com/" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header><AuthHeader xmlns="http://myemployer.com/"><Token>blah</Token></AuthHeader></SOAP-ENV:Header>
<ns1:Body><ns0:AwardBadgesBatch><ns0:badgeAwards/><ns0:includeSuccessStatus/></ns0:AwardBadgesBatch></ns1:Body></SOAP-ENV:Envelope>'
so badgeAwards and includeSuccessStatus are being sent empty. ???
EDIT: trying factory. This wsdl is odd (to me)--it defines the call itself as a type, thus:
<s:element name="AwardBadgesBatch">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="badgeAwards">
<s:complexType mixed="true">
<s:sequence>
<s:any/>
</s:sequence>
</s:complexType>
</s:element>
<s:element minOccurs="1" maxOccurs="1" name="includeSuccessStatus" type="s:boolean"/>
</s:sequence>
</s:complexType>
</s:element>
...
<wsdl:operation name="AwardBadgesBatch">
<soap:operation soapAction="http://pebble.searshc.com/AwardBadgesBatch" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
<soap:header message="tns:AwardBadgesBatchAuthHeader" part="AuthHeader" use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
<soap:header message="tns:AwardBadgesBatchAuthHeader" part="AuthHeader" use="literal"/>
</wsdl:output>
</wsdl:operation>
It does not define an AwardBadge type. So I just make my AwardBadge thus:
ba = Element('AwardBadge')
entid = Element('EnterpriseID').setText('jhoov11')
ba.append(entid)
btype = Element('badgeTypeID').setText('30')
ba.append(btype)
startdate = Element('startDate').setText('2013-8-22')
ba.append(startdate)
enddate = Element('endDate').setText('9999-12-31')
ba.append(enddate)
isdeleted = Element('isDeleted').setText('0')
ba.append(isdeleted)
and I set the values on my call
call = c.factory.create('AwardBadgesBatch')
call.badgeAwards= [ba]
call.includeSuccessStatus= False
and I call the service
c.service.AwardBadgesBatch(call)
and get some errors. When I look at what I sent I see
c.last_sent().plain()
u'<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:ns0="http://myemployer.com/" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<AuthHeader xmlns="http://myemployer.com/"><Token>blah</Token></AuthHeader>
</SOAP-ENV:Header>
<ns1:Body>
<ns0:AwardBadgesBatch>
<ns0:badgeAwards>
<AwardBadge/>
<ns0:includeSuccessStatus>false</ns0:includeSuccessStatus>
</ns0:badgeAwards>
<ns0:includeSuccessStatus/>
</ns0:AwardBadgesBatch>
</ns1:Body>
</SOAP-ENV:Envelope>'
So, three problems:
AwardBadge is empty.
the includeSuccessStatus is duplicated, one inside badgeAwards, where it doesn't belong.
the includeSuccessStatus that's in the right place is empty.
It doesn't make a difference whether I set badgeAwards to ba or [ba].
Any more insight?
Suds provides factory for service types - client.service.factory.create(type_name). That gives you python object. Here is demonstration, may be you can adapt it to your case:
...
client = Client(...)
award = client.factory.create("ns0:AwardBadgesBatch")
award.includeSuccessStatus = True
...
Suds has two nice methods - client.last_sent() and client.last_received(). Using them after request you can see what exactly did you send and received. Also, you can know a lot about service printing suds client:
client = Client(...)
print client
I think it is a bug in suds 0.4. Any updates done in MessagePlugins are simply not applied and the original message is send instead.
You can fix it by patching client.py in suds:
--- python2.7/site-packages/suds/client.py Sun Jan 08 16:58:36 2017 +0100
+++ client.py Thu Oct 05 11:04:20 2017 +0200
## -631,7 +631,8 ##
else:
soapenv = soapenv.plain()
soapenv = soapenv.encode('utf-8')
- plugins.message.sending(envelope=soapenv)
+ ctx = plugins.message.sending(envelope=soapenv)
+ soapenv = ctx.envelope
request = Request(location, soapenv)
request.headers = self.headers()
reply = transport.send(request)
Another option might be to switch to maybe switch to the jurko fork of suds, which also fixes this bug. However, I haven't tried that option myself.
The problem is also mentioned in this question: Suds Client ignores modification done by plugins into sending method
Related
I am making a SOAP webcall using suds-py3. This issue here is that the namespace for http://tempuri.org/ keeps switching between 'ns0' to'ns1'.
So before I pass my xml parameters I want to know which abbrevation it is accepting at the moment 'ns0' or 'ns1'
What I plan is to create an exception deliberately and parse through the exception output to get the abbreviation it is expecting. Below is my code. This gives me proper exception but when I try to get it into a variable it is not helping, it just gives me the class.
from suds import WebFault
c = client.Client(wsdl_url)
try:
c.service.getcategorylist()
except WebFault:
x=repr(WebFault)
it prints out the below
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://tempuri.org/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns1:Body>
<ns0:getcategorylist/>
</ns1:Body>
</SOAP-ENV:Envelope>
but when i try to check what is in x, it give below
"<class 'suds.WebFault'>"
I need the SOAP request part into a variable, so that I can get out the namespace abbreviation for http://tempuri.org/ 'ns0'
Thanks for help.
Ok ,Have found a solution to this issue.
wsdl_url = 'https://..?wsdl'
c = client.Client(wsdl_url)
# call service without any parameters to get error
try:
c.service.getcategorylist()
except:
pass
# this will give the last call details
x = str(c.last_sent())
X will save the last call details as below
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://tempuri.org/" xmlns:SOAP-
NV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns1:Body>
<ns0:getcategorylist/>
</ns1:Body>
</SOAP-ENV:Envelope>
extract the currently used node abbreviation
ix = x.find('="http://tempuri.org/"')
node = x[ix - 3:ix]
Now node holds the latest node abbreviation 'ns0/ns1' and I use this throughout the other code to make real service requests
I'm trying to connect to a RESTful API and I'm hacing problems when building the XML request, for that I'm using Elementree library.
I have an example of the XML I have to send in the request. From that example a build a model and then write the different attributes by code. But the output XML is not exactly like the example I was given and I'm unable to connect to the API.
This is the example I have:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetLoc xmlns="http://abc/Getloc">
<request>
<Access>
<string xmlns="http://bcd/Arrays"></string>
</Access>
<Details xsi:type="Request">
<Postcode ></Postcode >
</Details>
<UserConsent>Yes</UserConsent>
</request>
</GetLoc>
</soap:Body>
</soap:Envelope>
This is my code:
tree = ET.parse('model.xml')
root = tree.getroot()
ns = {'loc':'http://abc/Getloc',\
'arr':http://bcd/Arrays',\
'soapenv':'http://schemas.xmlsoap.org/soap/envelope/', \
'xsi':"http://www.w3.org/2001/XMLSchema-instance", \
xsd': "http://www.w3.org/2001/XMLSchema"}
tree.find('.//arr:string', ns).text = 'THC'
tree.find('.//Postcode ', ns).text = '15478'
This is the output XML (SOAP):
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://abc/Getloc" xmlns:ns2="http://bcd/Arrays" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Body>
<ns1:GetLoc >
<ns1:request>
<ns1:Access>
<ns2:string>THC</ns2:string>
</ns1:Access>
<ns1:Details xsi:type="Request">
<ns1:Postcode >15478</ns1:Postcode >
</ns1:Details>
<ns1:UserConsent>Yes</ns1:UserConsent>
</ns1:request>
</ns1:GetLoc >
</ns0:Body>
</ns0:Envelope>
With the example (first above) I have no problem when connecting to the API. However with the second one I get and error:
" status="Service Not Found. The request may have been sent to an invalid URL, or intended for an unsupported operation." xmlns:l7="http://www.layer7tech.com/ws/policy/fault"/>"
Both XML are sent to the same URL with the same headers and auth. I see both XML equivalent so I was expecting same behavior. I don't understand why it isn't working.
EDIT: The output XML needs to be like
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://abc/Getloc" xmlns:ns2="http://bcd/Arrays" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Body>
<ns1:GetLoc >
<ns1:request>
<ns1:Access>
<ns2:string>THC</ns2:string>
</ns1:Access>
<ns1:Details xsi:type="ns1:Request">
<ns1:Postcode >15478</ns1:Postcode >
</ns1:Details>
<ns1:UserConsent>Yes</ns1:UserConsent>
</ns1:request>
</ns1:GetLoc >
</ns0:Body>
</ns0:Envelope>
But I don't know hoy to change the code to get: xsi:type="ns1:Request"
Finally I found the solution myself.
The solution is in here (an incredibly complete article), since I was already using ElementTree. You may find other solutions like using lxml library.
So, for ElementTree I just need to use my own parser instead of the standard ElementTree.parse('file.xml').
The xsi attribute name is handled by the parser, but the parser doesn’t know that the attribute happens to contain a qualified name as well, so it leaves it as is. To be able to handle such a format, you can use a custom parser that knows how to handle certain attributes and elements, or keep track of the prefix mapping for each element.
To do the latter, you can use the iterparse parser, and ask it to report “start-ns” and “end-ns” events. The following snippet adds an ns_map attribute to each element which contains the prefix/URI mapping that applies to that specific element:
def parse_map(file):
events = "start", "start-ns", "end-ns"
root = None
ns_map = []
for event, elem in ET.iterparse(file, events):
if event == "start-ns":
ns_map.append(elem)
elif event == "end-ns":
ns_map.pop()
elif event == "start":
if root is None:
root = elem
elem.ns_map = dict(ns_map)
return ET.ElementTree(root)
I found this question: https://mail.python.org/pipermail/soap/2013-June/001120.html
I have the same problem, and can't find an answer. Please help.
I am in the process of implementing some existing WSDL in spyne, and I'm
running into a problem where I have requests that contain multiple
namespaces. For example, I have a request that looks like:
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
<a:Foo xmlns:a="www.example.com/schema/a" AttrA="a1" AttrB="b2">
<b:Baz xmlns:b="www.example.com/schema/b" AttrC="c3"/>
<a:Bar>blah</a:Bar>
</a:Foo>
</soapenv:Body>
</soapenv:Envelope>
When I send this request, I get the following back:
<?xml version='1.0' encoding='utf-8'?>
<senv:Envelope xmlns:senv="schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<senv:Fault>
<faultcode>senv:Client.SchemaValidationError</faultcode>
<faultstring>
<string>:1:0:ERROR:SCHEMASV:SCHEMAV_ELEMENT_CONTENT:
Element '{www.example.com/schema/b}Baz': This element
is not expected. Expected is one of (
{www.example.com/schema/a}Baz,
{www.example.com/schema/a}Bar ).</faultstring>
<faultactor></faultactor>
</senv:Fault>
</senv:Body>
</senv:Envelope>
I've been looking at the documentation and setting options, and so far
nothing has gotten me past this bump. Is this currently possible with
spyne? Do I need to do more and parse the in_document? Any input would be
greatly appreciated.
An for more detail the code I've been messing with:
from spyne.model.primitive import Unicode
from spyne.model.complex import Iterable, XmlAttribute, ComplexModel,
ComplexModelMeta, ComplexModelBase
from spyne.service import ServiceBase
from spyne.protocol.soap import Soap11
from spyne.application import Application
from spyne.decorator import srpc, rpc
class BazBase(ComplexModelBase):
__namespace__ = "www.example.com/schema/b"
__metaclass__ = ComplexModelMeta
class Baz(BazBase):
Thing = Unicode
AttrC = XmlAttribute(Unicode)
class FooService(ServiceBase):
__namespace__ = "www.example.com/schema/a"
#rpc(XmlAttribute(Unicode), XmlAttribute(Unicode), Baz, Unicode,
_returns=Iterable(Unicode))
def Foo(ctx, AttrA, AttrB, Baz, Bar):
yield 'Hello, %s' % Bar
app = Application([FooService],
"www.example.com/schema/a",
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11(),
)
Thanks!
So, here's how it's supposed to work:
from spyne import Unicode, Iterable, XmlAttribute, ComplexModel, \
ServiceBase, Application, rpc
from spyne.protocol.soap import Soap11
NS_B = "www.example.com/schema/b"
class Baz(ComplexModel):
__namespace__ = NS_B
Thing = Unicode
AttrC = XmlAttribute(Unicode)
class FooCustomRequest(ComplexModel):
AttrA = XmlAttribute(Unicode)
AttrB = XmlAttribute(Unicode)
Bar = Baz.customize(sub_ns=NS_B)
Baz = Unicode
class FooService(ServiceBase):
#rpc(FooCustomRequest, _returns = Iterable(Unicode),
_body_style='bare')
def Foo(ctx, req):
AttrA, AttrB, Baz, Bar = \
req.AttrA, req.AttrB, req.Baz, req.Bar
yield 'Hello, %s' % Bar
application = Application([FooService],
tns="www.example.com/schema/a",
in_protocol=Soap11(validator='soft'),
out_protocol=Soap11(),
)
But it doesn't. This generates the following object definition:
<xs:complexType name="FooCustomRequest">
<xs:sequence>
<xs:element name="Bar" type="s0:Baz" minOccurs="0" nillable="true"/>
<xs:element name="Baz" type="xs:string" minOccurs="0"
nillable="true"/>
</xs:sequence>
<xs:attribute name="AttrA" type="xs:string"/>
<xs:attribute name="AttrB" type="xs:string"/>
</xs:complexType>
As you see, the sub_ns declaration we make above is ignored by Spyne's schema generator.
<Addendum>
My Xml is rusty but after further research this seems to be the case by design -- as the name attribute of xs:element can not have a namespace prefix (ie it's a NCName), it's not possible to model the kind of document your client is sending you using the Xml Schema technology and friends. Your best bet at this point is soft validation, if you can't persuade your client to send a "correct" request.
</Addendum>
So validator='lxml' will never accept your document. However, validator='soft' will and you can use it until this bug is fixed in Spyne.
I can confirm the following request works:
<SOAP-ENV:Envelope xmlns:b="www.example.com/schema/b"
xmlns:a="www.example.com/schema/a"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<soapenv:Body>
<a:Foo AttrA="attr-a">
<b:Bar AttrC="attr-c">
<b:Thing>thing</b:Thing>
</b:Bar>
<a:Baz>baz</a:Baz>
</a:Foo>
</soapenv:Body>
</SOAP-ENV:Envelope>
If you can file an issue at https://github.com/arskom/spyne with the XSD fragment this needs to generate, I can fix it.
<Addendum2>
I'm persuaded that a schema can only define elements in its targetNamespace. It should be possible to have immediate children of a complexType from another namespace by using an <element ref="b:Baz" /> but that's nothing more than a theory. Again, if you know the kind of schema document this needs to generate, please file an issue. Otherwise the workaround with soft validation is your best bet at the moment.
</Addendum2>
I have a wsld definition that looks like
...
<sequence>
<element name="version" nillable="false" type="xsd:string"/>
<element name="payment" nillable="false" type="tns1:payment"/>
...
</sequence>
...
This is the xml log of the request that is sent
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://impl.ws.payline.experian.com" ...>
<SOAP-ENV:Header/>
<ns2:Body>
<ns0:doWebPaymentRequest>
<ns0:version>
<ns0:version>4</ns0:version>
<ns0:payment>
<ns1:amount>33300</ns1:amount>
...
</ns0:payment>
</ns0:version>
...
So suds encloses a payment object into version (a string), and it breaks the request.
Why is that ?? Any way to go around this ?
For those wondering :
suds seems buggy when it comes to setting objects on the fly. But it seems to work fine for sending bare xml.
So what I did is have an xml file and replace part of it with what I need, and send it
file: obj.xml
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://impl.ws.payline.experian.com" ...>
...
<xxx>REPLACE_ME</xxx>
...
and the script :
client = Client(url='file://path/to/.wsdl')
xml_request = open('/path/to/obj.xml', 'rb').read()
xml_request = xml_request.replace('REPLACE_ME', value)
result = client.service.TheService(__inject={'msg': xml_request})
I use python/suds to implement a client and I get wrong namespace prefixes in the sent SOAP header for a spefic type of parameters defined by element ref= in the wsdl.
The .wsdl is referencing a data types .xsd file, see below. The issue is with the function GetRecordAttributes and its first argument of type gbt:recordReferences.
File: browse2.wsdl
<xsd:schema targetNamespace="http://www.grantadesign.com/10/10/Browse" xmlns="http://www.grantadesign.com/10/10/Browse" xmlns:gbt="http://www.grantadesign.com/10/10/GrantaBaseTypes" elementFormDefault="qualified" attributeFormDefault="qualified">
<xsd:import schemaLocation="grantabasetypes2.xsd" namespace="http://www.grantadesign.com/10/10/GrantaBaseTypes"/>
<xsd:element name="GetRecordAttributes">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="gbt:recordReferences">
</xsd:element>
Referenced File : grantabasetypes2.xsd
<element name="recordReferences">
<complexType>
<sequence>
<element name="record" minOccurs="0" maxOccurs="unbounded" type="gbt:MIRecordReference"/>
</sequence>
</complexType>
</element>
SOAP Request sent by suds:
<SOAP-ENV:Envelope xmlns:ns0="http://www.grantadesign.com/10/10/GrantaBaseTypes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.grantadesign.com/10/10/Browse" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns1:Body>
<ns2:GetRecordAttributes>
<ns2:recordReferences>
<ns0:record>
</ns0:record>
</ns2:recordReferences>
</ns2:GetRecordAttributes>
</ns1:Body>
</SOAP-ENV:Envelope>
Problem : <ns2:recordReferences> has wrong prefix, should be <ns0:recordReferences> since it belongs to the namespace ...GrantaBaseTypes defined in the .xsd.
This happens for all arguments defined by ref= in the wsdl. How can this be automatically fixed?
Note: I checked that the "good" prefix is accepted by the service by manually sending the xml SOAP request via curl.
UPDATE
I meddled with SUDS source code and the following empirical fix forces all elements with ref= attribute to assume the ref-ed namespace (previously, they take on the schema root namespace or whatever tns is):
File: /suds/xsd/sxbase.py
class SchemaObject(object):
....
def namespace(self, prefix=None):
ns = self.schema.tns
#FIX BEGIN
if self.ref and self.ref in self.schema.elements.keys():
ns = self.ref
#FIX END
Works with my service, but I'm not sure if it'll break other things. I would prefer a smarter solution that does not change SUDS source code.
Thanks,
Alex
Write a Suds plugin to modify the XML before it is sent.
from suds.client import Client
from suds.plugin import MessagePlugin
class MyPlugin(MessagePlugin):
def marshalled(self, context):
#modify this line to reliably find the "recordReferences" element
context.envelope[1][0][0].setPrefix('ns0')
client = Client(WSDL_URL, plugins=[MyPlugin()])
Quoting Suds documentation:
marshalled()
Provides the plugin with the opportunity to inspect/modify the envelope Document before it is sent.
I had the exact same problem when using suds to access a BizTalk/IIS SOAP service.
From what I can tell from the WSDL it occurs when there is a "complexType" that is not part of the "targetNamespace" (it has it's own), which has a child that is also a complexType, but with no namespace set. In BizTalk this means that the child should belong to the same namespace as the parent, but Suds seem to think that it then should be part of the targetNamespace ....
The fix in the source-code solved the thing "correctly", but since I want to be able to upgrade without applying the fix every time I went for another solution....
My solution was to skip Suds and just copy the raw XML, use that as a template and copy the values into it ... Not beautiful, but at least simple.
The solution to add a plugin is in my opinion equally hardcoded and perhaps even harder to maintain.
You could build soap message yourself and use SoapClient to send the message :
sc = SoapClient(cli.service.XXXMethod.client,cli.service.XXXMethod.method)
sc.send(some_soap_doc)
I prefer regular expressions :)
import re
class EnvelopeFixer(MessagePlugin):
def sending(self, context):
# rimuovi i prefissi
context.envelope = re.sub( 'ns[0-9]:', '', context.envelope )
return context.envelope