SUDS 生成的 XML 不正确

发布于 2024-08-31 13:50:31 字数 3064 浏览 2 评论 0原文

我正在尝试使用 SUDS 和 Python 与 SOAP Web 服务进行通信。经过大量学习 Python(是的,我是新手)并研究如何使用 SUDS 之后,我遇到了一个问题。

根据 suds,我正在调用的 Web 方法的签名是

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

如您所见,有一个 '分类事件代码' 元素周围所有其他元素,这不应该在那里。如果我将此 xml 剪切并粘贴到 SOAPUI 中,首先删除此元素,然后将其直接发布到 Web 服务,则它可以成功运行。

这是我用来拨打电话的代码

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。

谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

著墨染雨君画夕 2024-09-07 13:50:32

我遇到了完全相同的问题。我的 SOAP 请求中的参数序列被包装在与第一个参数同名的元素中。例如,

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

我已经检查了 WSDL 以确保它没有问题。

问题似乎是因为我使用 client.factory.create 方法创建了一个 CreationReq 对象。通过打印来检查客户端表明我正在调用的方法不将该对象作为参数。相反,它需要一个命名参数列表。

所以我的代码是:

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)

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)
我还不会笑 2024-09-07 13:50:32

如果您为 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、优先级、标题类型等。

因此您不想这样做:(将 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/ )将有助于揭晓答案。

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.

鹿港巷口少年归 2024-09-07 13:50:32

你打算用这个作为
配置文件,或存储信息。
或者这是为了跨网络发送数据
网络?

好吧,如果是这样的话,为什么不使用 json 或 json-rpc,据说它们更快,更容易解析,也更容易阅读。 XML 是一种可怕的数据类型,我个人等不及它消亡,如果您正在寻找数据发送,那么使用 json 是非常值得的。

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.

鲜血染红嫁衣 2024-09-07 13:50:32

您创建该元素两次。删除这个:

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

并更改这个:

newcase.ClassificationEventCode = 2000023

这应该删除那个额外的标签。

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.

后来的我们 2024-09-07 13:50:32

我发现这个线程正在寻找同一问题的解决方案。到目前为止,我已经研究过只有当您将工厂创建的对象直接传递给服务方法时才会发生这种情况。并且仅适用于使用扩展(继承)的 wsdl 数据类型。

我能想到的解决方案还有更多。

  • 根本不要将工厂用于顶级类型。
  • 编写suds插件在生成后更改xml
  • 重新编写wsdl以不使用继承(扩展标记)
  • 在传递到服务方法之前更改对象类型

我选择了最后一个,因为它是最简单的方法。所以就有了代码。

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

像这样使用。

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

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文