itworkedonmachine
itworkedonmachine

Reputation: 43

Incorrect XML produced by SUDS

I am trying to talk to a SOAP web service using SUDS and Python. After lots of messing around learning Python (yes I am new to this) and working out how to use SUDS I have come across a problem.

The signature of the web method I am calling, according to suds, 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 that I want and send it to the method. However I get an error. So I turned logging on and I can see that the XML that is being sent is not correct which is causing a deserialize error. The SOAP package looks like the following

<?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 the other elements, this should not be there. If I cut and paste this xml into SOAPUI and first remove this element 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 ideas why this is happening? I guess SUDS thinks that it should be there based on the WSDL.

Thanks.

Upvotes: 3

Views: 5811

Answers (5)

gjask
gjask

Reputation: 304

I've found this thread searching for the solution of the same problem. So far I've researched it happens only when you pass factory created object directly to service method. And only with wsdl data types using extension (inheritance).

There is more solutions I could think of.

  • don't use factory for top level type at all.
  • write suds plugin changing xml after generating
  • re-write wsdl to not use inheritance (extension tag)
  • change type of object before passing to service method

I've chose the last one, as it is easiest way. So there is the code.

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

Use like this.

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

Upvotes: 0

Robin Elvin
Robin Elvin

Reputation: 1255

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

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

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

The problem it seems is because 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 that object as a parameter. Rather it takes a list of named args.

So my code was:

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

Now it is:

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

Upvotes: 6

mgav
mgav

Reputation: 23

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

For example:

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 needs to be passed in the service call to createCase. Even though the WSDL may define the method as needing a 'FWTCaseCreate', suds may be picking up the definition for createCase to need ClassificationEventCode, Priority, Title types, etc.

Therefore you wouldn't want to do: (which passes in newCase for the first argument, putting all the details under that tag)

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

But instead call the service like so: (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__])

Passing in the list of args that are required for the service call.

I don't know why the service call definition is incorrectly resolved as being what it is, but passing in the correct object doesn't expand automatically to the correct argument list needed. Perhaps reading the suds source ( http://jortel.fedorapeople.org/suds/doc/ ) would help divulge the answer.

Upvotes: 1

chrisg
chrisg

Reputation: 41695

Your creating the element twice. Remove this:

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

And change this:

newcase.ClassificationEventCode = 2000023

This should remove that extra tag.

Upvotes: 0

Taos
Taos

Reputation: 32

Are you going to be using this as a config file, or to store infomation. Or is this to send data across the web?

Okay then, if this is so then why not use json or json-rpc, they are said to be much faster, easier to parse and far easier to read. XML is a horible data type and I personally can't wait till it dies, if your looking for data sending it would be well worth using the json's.

Upvotes: 0

Related Questions