SUDS生成的XML不正确

3 投票
5 回答
5758 浏览
提问于 2025-04-15 22:33

我正在尝试用 SUDS 和 Python 与一个 SOAP 网络服务进行通信。经过一番折腾学习 Python(没错,我是新手)以及弄明白如何使用 SUDS,我遇到了一个问题。

根据 SUDS 的说法,我调用的网络方法的签名是

(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
}

所以我用 SUDS 创建了我想要的类,并把它发送到这个方法。但是我遇到了一个错误。于是我开启了日志功能,发现发送的 XML 不正确,这导致了反序列化错误。SOAP 包的结构如下

<?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>

正如你所看到的,所有其他元素外面有一个 'ClassificationEventCode' 的元素,这个元素是不应该存在的。如果我把这个 XML 复制到 SOAPUI 中,先去掉这个元素,然后直接发送到网络服务,就能成功。

这是我用来进行调用的代码

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)

有没有人知道为什么会出现这个问题?我想 SUDS 可能是根据 WSDL 认为这个元素应该存在。

谢谢。

5 个回答

0

你打算把这个当作配置文件,还是用来存储信息?或者是用来在网上传输数据呢?

那么,如果是这样的话,为什么不使用json或者json-rpc呢?它们被认为速度更快,解析起来更简单,而且更容易阅读。XML这种数据格式真是糟糕,我个人非常期待它的消亡。如果你是想传输数据,使用json会更划算。

1

如果你为你的suds服务创建一个客户端,有一些属性可以帮助你判断在服务调用时需要传入哪些对象。

比如说:

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

这应该能显示出前缀、端口、方法和类型。对于你的代码,你应该能看到在调用createCase服务时需要传入什么。尽管WSDL可能定义这个方法需要一个'FWTCaseCreate',但suds可能会根据createCase的定义,要求传入ClassificationEventCode、Priority、Title等类型。

所以你不应该这样做:(这会把newCase作为第一个参数传入,把所有细节放在这个标签下)

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

而是应该这样调用服务:(根据服务的定义)

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

或者可能是:

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

传入服务调用所需的参数列表。

我不知道为什么服务调用的定义会错误地解析成现在这个样子,但传入正确的对象并不会自动扩展为所需的正确参数列表。也许阅读一下suds的源代码(http://jortel.fedorapeople.org/suds/doc/)会帮助你找到答案。

6

我之前也遇到过完全一样的问题。在我的SOAP请求中,参数的顺序被包裹在一个和第一个参数同名的元素里。例如:

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

我检查过WSDL,确认没有问题。

看起来问题出在我使用client.factory.create方法创建了一个CreationReq对象。通过打印client来检查,发现我调用的方法并不接受那个对象作为参数。实际上,它接受的是一组命名参数。

所以我之前的代码是:

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

现在改成:

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

撰写回答