Suds 生成空元素;如何删除它们? [英] Suds generates empty elements; how to remove them?

查看:24
本文介绍了Suds 生成空元素;如何删除它们?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[根据两天前第 1 个帖子以来的经验进行的主要修改.]

[Major Edit based on experience since 1st post two days ago.]

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

I am building a Python SOAP/XML script using Suds, but am struggling to get the code to generate SOAP/XML that is acceptable to the server. I had thought that the issue was that Suds was not generating prefixes for inner elements, but subsequently it turns out that the lack of prefixes (see Sh-Data and inner elements) is not an issue, as the Sh-Data and MetaSwitchData elements declare appropriate namespaces (see below).

<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/> 元素不是允许值之一.

But this still fails. The issue is that Suds generates empty elements for optional elements (marked as Mandatory = No in the WSDL). But the server requires that an optional element is either present with a sensible value or absent, and I get the following error (because the <CallFeatureServerControlStatus/> element is not one of the allowable values.

所提供的用户数据未针对用户数据的 MetaSwitch XML 架构进行验证.
详细信息:cvc-enumeration-valid:值 '' 对于枚举 '[Controlling, Abandoned, Cautiously controls]' 不是方面有效的.它必须是枚举中的一个值.

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 并删除空元素,则请求工作正常.

If I take the generated SOAP/XML into SOAPUI and delete the empty elements, the request works just fine.

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

Is there a way to get Suds to either not generate empty elements for optional fields, or for me to remove them in code afterwards?

重大更新

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

I have solved this problem (which I've seen elsewhere) but in a pretty inelegant way. So I am posting my current solution in the hope that a) it helps others and/or b) someone can suggest a better work-around.

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

It turns out that the problem was not that Suds generates empty elements for optional elements (marked as Mandatory = No in the WSDL). But rather that that Suds generates empty elements for optional complex elements. For example the following Meta_SubG_BaseInformation elements are simple elements and Suds does not generate anything for them in the 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 中.

In contrast the following Meta_SubG_BaseInformation element is a complex element, and even when it is optional and my code does not assign a value to it, it ends up in the generated 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 生成以下内容(如上所述),这会扰乱我的服务器.

Suds generates the following for ProviderStatus which (as stated above) upsets my server.

<ProviderStatus/>

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

The work-around is to set all Meta_SubG_BaseInformation elements to None after creating the parent element, and before assigning values, as in the following. This is superfluous for the simple elements, but does ensure that non-assigned complex elements do not result in generated 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,这是我的服务器可以接受的.

This results in Suds generating SOAP/XML without empty elements, which is acceptable to my server.

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

But does anyone know of a cleaner way to achieve the same effect?

以下解决方案基于以下杜桑和罗兰史密斯的回答/评论.

该解决方案使用 Suds MessagePlugin 在 Suds 将请求发送到网络之前修剪 <SubscriberType/> 形式的空"XML.我们只需要修剪 ShUpdates(我们在服务器上更新数据的地方),并且逻辑(尤其是索引到子项以获取服务指示元素列表)非常特定于 WSDL.它不适用于不同的 WSDL.

This solution uses a Suds MessagePlugin to prune "empty" XML of the form <SubscriberType/> before Suds puts the request on the wire. We only need to prune on ShUpdates (where we are updating data on the server), and the logic (especially the indexing down into the children to get the service indication element list) is very specific to the WSDL. It would not work for different 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)

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

And then we just need to reference the plugin when we create the client.

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

推荐答案

您可以使用正则表达式过滤掉空元素.

You can filter the empty elements out with a regular expression.

假设您的 XML 数据在字符串 xmltext 中;

Assuming your XML data is in the string xmltext;

import re
filteredtext = re.sub('s+<.*?/>', '', xmltext)

这篇关于Suds 生成空元素;如何删除它们?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆