如何让suds发送可选的空元素?

2 投票
2 回答
1214 浏览
提问于 2025-04-18 06:15

我有一个SOAP API,正在尝试通过suds(一个Python库)调用一个方法:

DoSomething(ns1:DoSomethingRequest Input, )

DoSomethingRequest看起来像这样:

    (DoSomethingRequest){
   Person = 
      (Person){
         Name = "Joe"
         Age = 32
      }
   ActionOne = None
   ActionTwo = None
   ActionThree = None
 }

在类型定义中,动作参数都是可选的。要调用特定的动作,你需要设置DoSomethingRequest.ActionOne = [一个ActionOneRequest的实例]。这没问题(我可以执行ActionOne),但我想调用ActionThree,而ActionThreeRequest是一个空的复杂元素。当我设置DoSomethingRequest.ActionThree = ActionThreeRequest时,打印DoSomethingRequest的结果是:

    (DoSomethingRequest){
   Person = 
      (Person){
         Name = "Joe"
         Age = 32
      }
   ActionOne = None
   ActionTwo = None
   ActionThree = <empty>
 }

而发送到服务器的XML中没有ActionThree。如果我用pdb拦截代码并添加一个空元素<ActionThree></ActionThree>,它就能工作了。

看看suds的代码:

class ObjectAppender(Appender):
    """
    An L{Object} appender.
    """

    def append(self, parent, content):
        object = content.value
        if self.optional(content) and footprint(object) == 0:
            return
        child = self.node(content)
        parent.append(child)
        for item in object:
            cont = Content(tag=item[0], value=item[1])
            Appender.append(self, child, cont)

还有

def footprint(sobject):
    """
    Get the I{virtual footprint} of the object.
    This is really a count of the attributes in the branch with a significant value.
    @param sobject: A suds object.
    @type sobject: L{Object}
    @return: The branch footprint.
    @rtype: int
    """
    n = 0
    for a in sobject.__keylist__:
        v = getattr(sobject, a)
        if v is None: continue
        if isinstance(v, Object):
            n += footprint(v)
            continue
        if hasattr(v, '__len__'):
            if len(v): n += 1
            continue
        n +=1
    return n

我不常用SOAP,所以我猜我可能是错误地使用了API,或者是错误地使用了suds。或者,也许这个服务的API不太标准。

你知道为什么会有这个问题吗?最好的解决办法是什么?

奇怪的是,SO上有一个相反的问题:Suds生成空元素;如何去掉它们? 不幸的是,去掉空元素要简单得多,而弄清楚哪些元素被去掉并重新添加它们就难多了。

谢谢!

2 个回答

1

如果有其他人遇到这个问题,找不到答案的话,我来分享一下我的经历。我当时在尝试发送一个SOAP请求的内容,

<ns0:list><ns0:filter xsi:type="ns0:UserFilter"/></ns0:list>

第一次尝试时,我用 suds.client.factory 创建了一个元素,然后发送时没有做任何配置。结果生成了下面这个内容,

<ns0:list><filter/></ns0:list>

接着我又试着手动创建一个 Element,并手动设置 type 属性。结果还是得到了同样的内容。

后来我查看了源代码,发现 SoapClient.send() 方法在 sax.Document.plain() 方法中把我的属性给去掉了。

看了代码后,我发现可以在suds客户端上设置 prettyxml=True 这个选项,这样就不会去掉属性了。

2

在阅读了相关规范并与专家交流后,我没有发现任何说明说SOAP库可以去掉可选的、空的元素。

github上有一个修复过的suds版本。

撰写回答