python - Suds 生成空元素;如何删除它们?

标签 python soap suds

[根据两天前第一篇文章以来的经验进行的主要编辑。]

我正在使用 Suds 构建一个 Python SOAP/XML 脚本,但我正在努力获取代码来生成服务器可接受的 SOAP/XML。我原以为问题是 Suds 没有为内部元素生成前缀,但后来发现缺少前缀(参见 Sh-Data 和内部元素)不是问题,因为 Sh-DataMetaSwitchData元素声明适当的命名空间(见下文)。

<SOAP-ENV:Envelope xmlns:ns3="http://www.metaswitch.com/ems/soap/sh" xmlns:ns0="http://www.metaswitch.com/ems/soap/sh/userdata" xmlns:ns1="http://www.metaswitch.com/ems/soap/sh/servicedata" xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns2:Body>
      <ns3:ShUpdate>
         <ns3:UserIdentity>Meribel/TD Test Sub Gateway 3</ns3:UserIdentity>
         <ns3:DataReference>0</ns3:DataReference>
         <ns3:UserData>
            <Sh-Data xmlns="http://www.metaswitch.com/ems/soap/sh/userdata">
               <RepositoryData>
                  <ServiceIndication>Meta_SubG_BaseInformation</ServiceIndication>
                  <SequenceNumber>0</SequenceNumber>
                  <ServiceData>
                     <MetaSwitchData xmlns="http://www.metaswitch.com/ems/soap/sh/servicedata" IgnoreSequenceNumber="False" MetaSwitchVersion="?">
                        <Meta_SubG_BaseInformation Action="apply">
                           <NetworkElementName>Meribel</NetworkElementName>
                           <Description>TD Test Sub Gateway 3</Description>
                           <DomainName>test.datcon.co.uk</DomainName>
                           <MediaGatewayModel>Cisco ATA</MediaGatewayModel>
                           <CallFeatureServerControlStatus/>
                           <CallAgentControlStatus/>
                           <UseStaticNATMapping/>
                           <AuthenticationRequired/>
                           <ProviderStatus/>
                           <DeactivationMode/>
                        </Meta_SubG_BaseInformation>
                     </MetaSwitchData>
                  </ServiceData>
               </RepositoryData>
            </Sh-Data>
         </ns3:UserData>
         <ns3:OriginHost>user@domain.com?clientVersion=7.3</ns3:OriginHost>
      </ns3:ShUpdate>
   </ns2:Body>
</SOAP-ENV:Envelope>

但这仍然失败。问题是 Suds 为可选元素生成空元素(在 WSDL 中标记为 Mandatory = No)。但是服务器要求可选元素要么以合理的值存在,要么不存在,我收到以下错误(因为 <CallFeatureServerControlStatus/> 元素不是允许的值之一。

The user data provided did not validate against the MetaSwitch XML Schema for user data.
Details: cvc-enumeration-valid: Value '' is not facet-valid with respect to enumeration '[Controlling, Abandoned, Cautiously controlling]'. It must be a value from the enumeration.

如果我将生成的 SOAP/XML 放入 SOAPUI 并删除空元素,请求就可以正常工作。

有没有办法让 Suds 不为可选字段生成空元素,或者让我之后在代码中删除它们?

主要更新

我已经解决了这个问题(我在别处看到过),但方式很不雅。因此,我发布了我当前的解决方案,希望 a) 它可以帮助其他人和/或 b) 有人可以提出更好的解决方案。

事实证明,问题不在于 Suds 为可选元素(在 WSDL 中标记为 Mandatory = No)生成空元素。而是 Suds 为可选的 complex 元素生成空元素。例如,以下 Meta_SubG_BaseInformation 元素是简单元素,Suds 不会在 SOAP/XML 中为它们生成任何内容。

<xs:element name="CMTS" type="xs:string" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName firstVersion="5.0" lastVersion="7.4">CMTS</d:DisplayName>
            <d:ValidFrom>5.0</d:ValidFrom>
            <d:ValidTo>7.4</d:ValidTo>
            <d:Type firstVersion="5.0" lastVersion="7.4">String</d:Type>
            <d:BaseAccess firstVersion="5.0" lastVersion="7.4">RWRWRW</d:BaseAccess>
            <d:Mandatory firstVersion="5.0" lastVersion="7.4">No</d:Mandatory>
            <d:MaxLength firstVersion="5.0" lastVersion="7.4">1024</d:MaxLength>
        </xs:documentation>
    </xs:annotation>
</xs:element>

<xs:element name="TAGLocation" type="xs:string" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName>Preferred location of Trunk Gateway</d:DisplayName>
            <d:Type>String</d:Type>
            <d:BaseAccess>RWRWRW</d:BaseAccess>
            <d:Mandatory>No</d:Mandatory>
            <d:DefaultValue>None</d:DefaultValue>
            <d:MaxLength>1024</d:MaxLength>
        </xs:documentation>
    </xs:annotation>
</xs:element>

相比之下,下面的 Meta_SubG_BaseInformation 元素是一个复杂的元素,即使它是可选的并且我的代码没有为其赋值,它最终也会出现在生成的 SOAP/XML 中。

<xs:element name="ProviderStatus" type="tMeta_SubG_BaseInformation_ProviderStatus" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName>Provider status</d:DisplayName>
            <d:Type>Choice of values</d:Type>
            <d:BaseAccess>R-R-R-</d:BaseAccess>
            <d:Mandatory>No</d:Mandatory>
            <d:Values>
                <d:Value>Unavailable</d:Value>
                <d:Value>Available</d:Value>
                <d:Value>Inactive</d:Value>
                <d:Value>Active</d:Value>
                <d:Value>Out of service</d:Value>
                <d:Value>Quiescing</d:Value>
                <d:Value>Unconfigured</d:Value>
                <d:Value>Pending available</d:Value>
            </d:Values>
        </xs:documentation>
    </xs:annotation>
</xs:element>

Suds 为 ProviderStatus 生成以下内容(如上所述)扰乱了我的服务器。

<ProviderStatus/>

解决方法是设置所有 Meta_SubG_BaseInformation元素 None在创建父元素之后,在赋值之前,如下所示。这对于简单元素来说是多余的,但确实确保未分配的复杂元素不会导致生成 SOAP/XML。

subGatewayBaseInformation = client.factory.create('ns1:Meta_SubG_BaseInformation')
for (el) in subGatewayBaseInformation:
  subGatewayBaseInformation.__setitem__(el[0], None)
subGatewayBaseInformation._Action            = 'apply'
subGatewayBaseInformation.NetworkElementName = 'Meribel'
etc...

这会导致 Suds 生成不含空元素的 SOAP/XML,这对我的服务器来说是可以接受的。

但是有没有人知道有更简洁的方法来达到同样的效果?

以下解决方案基于以下 dusan 和 Roland Smith 的回答/评论。

此解决方案使用 Suds MessagePlugin 来删除 <SubscriberType/> 形式的“空”XML在 Suds 将请求发送到电汇之前。我们只需要修剪 ShUpdates(我们在服务器上更新数据的地方),逻辑(特别是向下索引到子项以获取服务指示元素列表)是 WSDL 非常特定的。它不适用于不同的 WSDL。

class MyPlugin(MessagePlugin):
  def marshalled(self, context):
    pruned = []
    req = context.envelope.children[1].children[0]
    if (req.name == 'ShUpdate'):
      si = req.children[2].children[0].children[0].children[2].children[0].children[0]
      for el in si.children:
        if re.match('<[a-zA-Z0-9]*/>', Element.plain(el)):
          pruned.append(el)
      for p in pruned:
        si.children.remove(p)

然后我们只需要在创建客户端时引用插件即可。

client = Client(url, plugins=[MyPlugin()])

最佳答案

您可以使用插件在发送到服务器之前修改 XML(我的回答基于 Ronald Smith 的解决方案):

from suds.plugin import MessagePlugin
from suds.client import Client
import re

class MyPlugin(MessagePlugin):
    def sending(self, context):
        context.envelope = re.sub('\s+<.*?/>', '', context.envelope)


client = Client(URL_WSDL, plugins=[MyPlugin()])

引用 documentation :

The MessagePlugin currently has (5) hooks ::
(...)
sending()
Provides the plugin with the opportunity to inspect/modify the message text before it is sent.

基本上 Suds 会在发送 XML 之前调用 sending,因此您可以修改生成的 XML(包含在 context.envelope 中)。您必须将插件类 MyPlugin 传递给 Client 构造函数才能使其工作。

编辑

另一种方法是使用marshalled 修改XML 结构,删除空元素(未经测试的代码):

class MyPlugin(MessagePlugin):
    def marshalled(self, context):
        #remove empty tags inside the Body element
        #context.envelope[0] is the SOAP-ENV:Header element
        context.envelope[1].prune()

关于python - Suds 生成空元素;如何删除它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9388180/

相关文章:

python - 如何向日期时间对象添加缺失的秒数

python - SUDS 自定义 header

python - 如何添加带有泡沫的新对象?

python - 如何使用 python suds 客户端进行 kerberos 身份验证

python - Google App Engine 上的 IPv6 到 IPv4

python - Pandas pivot_table : cannot achieve correct format

php - 如何制作 PHP SOAP 客户端并将结果 xml 存储在 php 变量中

java - 如何在 wsdl 中定义根 SOAP 服务

c# - REST 服务代理与 WCF SOAP 代理的性能对比

python - Heroku H10错误— Python