Invalid XML generated by SUDS

I am trying to talk to a SOAP web service using SUDS and Python. After a lot of trouble learning Python (yes I'm new to this) and working out how to use SUDS, I ran into a problem.

The signature of the web method I am calling, according to the foam, is

(FWTCaseCreate){
ClassificationEventCode = None
Priority = None
Title = None
Description = None
Queue = None
DueDate = None
AssociatedObject = 
  (FWTObjectBriefDetails){
     ObjectID = 
        (FWTObjectID){
           ObjectType = None
           ObjectReference[] = <empty>
        }
     ObjectDescription = None
     Details = None
     Category = None
  }
Form = 
  (FWTCaseForm){
     FormField[] = <empty>
     FormName = None
     FormKey = None
  }
Internal = None
InteractionID = None
XCoord = None
YCoord = None
}

      

So I use SUDS to create the classes I want and send them to the method. However, I am getting an error. So I turned on registration and I see that the XML being sent is incorrect, which is causing the deserialization error. SOAP package looks like this

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.CRM.com/wsdl/FLTypes"    xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
   <wsse:Security>
      <wsse:BinarySecurityToken>eaadf1ddff99a8</wsse:BinarySecurityToken>
   </wsse:Security>
</SOAP-ENV:Header>
<ns1:Body>
   <ns0:FWTCaseCreate>
      <ClassificationEventCode>
         <ClassificationEventCode>2000023</ClassificationEventCode>
         <Priority>1</Priority>
         <Title>testing</Title>
         <Description>testing</Description>
         <Queue/>
         <Internal>True</Internal>
         <XCoord>356570</XCoord>
         <YCoord>168708</YCoord>
      </ClassificationEventCode>
   </ns0:FWTCaseCreate>
</ns1:Body>

      

As you can see there is a 'ClassificationEventCode' element around all other elements, this shouldn't be. If I cut and paste this xml into SOAPUI and remove this item first and then post it directly to the web service, it works successfully.

Here is the code I am using to make the call

client = Client(url)

#Add a header for the security
ssnns = ('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd')

ssn = Element('BinarySecurityToken', ns=ssnns).setText(binaryKey)

ssn1 = Element('Security',ns=ssnns)

ssn1.append(ssn)

client.set_options(soapheaders=ssn1) 

newCase = client.factory.create('ns1:FWTCaseCreate')

classEventCode = client.factory.create('ns1:FWTEventCode')
classEventCode.value = 2000023

newCase.ClassificationEventCode = classEventCode
newCase.Priority = 1
#optional
newCase.AssociatedObject = None
#optional
newCase.Form = None
#optional
newCase.Internal = None
#optional
newCase.InteractionID =  None
#optional
newCase.DueDate = None
#optional
newCase.Queue = None

newCase.Title = 'Title'

newCase.Description = 'description'

newCase.XCoord = '356570'

newCase.YCoord = '168708'

caseID = client.service.createCase(newCase)

      

Does anyone have any idea why this is happening? I think SUDS thinks it should be WSDL based.

Thanks.

+2


a source to share


5 answers


I was getting exactly the same problem. The sequence of parameters in my SOAP request is wrapped in an element with the same name as the first parameter. eg.

....
   <ns0:Body>
      <ns1:CreationReq>
         <ReqType>
            <ReqType>1</ReqType>
            <Title>Mr</Title>
            ....
         </ReqType>
      </ns1:CreationReq>
   </ns0:Body>
....

      

I checked the WSDL to make sure there is no problem with it.

The problem is that I created a CreationReq object using the client.factory.create method. Checking the client by printing it shows that the method I am calling does not take this object as a parameter. Rather, it takes a list of named args.



So my code is:

req = client.factory.create('CreationReq')
req.ReqType = 1
req.Title = 'Mr'
resp = client.service.Create(req)

      

Now this:

req = {}
req['ReqType'] = 1
req['Title'] = 'Mr'
resp = client.service.Create(**req)

      

+5


a source


If you are creating a client for your suds services, there are some attributes you can see to determine what objects are needed to pass the service call.

For instance:

import suds
client = suds.Client(url)
for a in client.sd: #print the whole service definition
    print a

      

This should show you the prefixes, ports with methods and types. For your code, you should be able to see what to pass in the createCase service call. Even though WSDL can define a method as needed for "FWTCaseCreate", foam can pick up a definition for createCase to use ClassificationEventCode, Priority, Title Types, etc.

So you don't want to do: (which passes the first argument to newCase, putting all the details under that tag)

newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase)

      



Instead, call the service like this: (based on the service definition)

newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase.ClassificationEventCode, newCase.Priority, ...)

      

Or maybe:

newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(*[getattr(newCase,a) for a in newCase.__keylist__])

      

Go to the list of arguments required to invoke the service.

I don't know why the definition of a service call is incorrectly resolved as what it is, but passing the correct object does not automatically expand to the required argument list. Perhaps reading the foam source ( http://jortel.fedorapeople.org/suds/doc/ ) will help divulge the answer.

+1


a source


You are going to use this as a config file or save information. Or is it data transmission over the web?

Ok then, if so, why not use json or json-rpc, they are said to be much faster, easier to parse and much easier to read. XML is a terrible datatype and I personally can't wait for it to die, if you are looking for sending data it would be helpful to use json's.

0


a source


By creating the item twice. Remove this:

classEventCode = client.factory.create('ns1:FWTEventCode') 
classEventCode.value = 2000023 

      

And change this:

newcase.ClassificationEventCode = 2000023

      

This should remove the extra tag.

0


a source


I found this thread looking for a solution to the same problem. So far, I've only researched this when you pass the generated factory object directly to the service method. And only with wsdl datatypes using extension (inheritance).

There are more solutions I could think of.

  • don't use a factory at all for the top level type.
  • write suds plugin changing xml after generation
  • overwrite wsdl to not use inheritance (extension tag)
  • change the type of the object before going to the service method

I chose the latter as it is the easiest way. So there is the code.

def sudsToDict(data):
    return dict([(str(key),val) for key,val in data])

      

Use this.

data = client.factory.create('wsdl_data_type')
# now fill with values and then
data = sudsToDict(data)
client.service.some_method(**data)

      

0


a source







All Articles