Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 泡沫产生空元素;如何移除它们?_Python_Soap_Suds - Fatal编程技术网

Python 泡沫产生空元素;如何移除它们?

Python 泡沫产生空元素;如何移除它们?,python,soap,suds,Python,Soap,Suds,[主要编辑基于两天前第一次发布后的经验。] 我正在使用Suds构建一个pythonsoap/XML脚本,但是我很难获得生成服务器可以接受的SOAP/XML的代码。我原以为问题在于sud没有为内部元素生成前缀,但后来发现缺少前缀(参见Sh Data和内部元素)不是问题,因为Sh Data和MetaSwitchData元素声明了适当的名称空间(参见下文) Meribel/TD测试子网关3 0 Meta_SubG_基本信息 0 梅瑞贝尔 TD测试子网关3 test.datcon.co.uk 思科AT

[主要编辑基于两天前第一次发布后的经验。]

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


Meribel/TD测试子网关3
0
Meta_SubG_基本信息
0
梅瑞贝尔
TD测试子网关3
test.datcon.co.uk
思科ATA
user@domain.com?客户端版本=7.3
但这仍然失败。问题是sud为可选元素生成空元素(在WSDL中标记为
Mandatory=No
)。但是服务器要求可选元素要么存在一个合理的值,要么不存在,我得到了以下错误(因为
元素不是允许的值之一)

提供的用户数据未根据用户数据的MetaSwitch XML架构进行验证。
详细信息:cvc枚举有效:值“”对于枚举“[控制、放弃、谨慎控制]”无效。它必须是枚举中的值

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

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

主要更新

我已经解决了这个问题(我在其他地方见过),但用了一种非常不雅观的方式。因此我发布了我目前的解决方案,希望a)它能帮助其他人和/或b)有人能提出更好的解决方案

事实证明,问题不在于sud为可选元素生成空元素(在WSDL中标记为
Mandatory=No
)。而是说,sud为可选的复杂的元素生成空元素。例如,下面的Meta_SubG_BaseInformation元素是简单的元素,sud不会在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...

CMT
5
7.4
一串
RW
不
1024
中继网关的首选位置
一串
RW
不
没有一个
1024
相比之下,下面的Meta_SubG_BaseInformation元素是一个复杂的元素,即使它是可选的,并且我的代码没有给它赋值,它也会在生成的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...

提供者状态
价值选择
R-R-R-
不
不可用的
可用
不活跃的
活跃的
停用
安静
未配置
待定可用
Suds为ProviderStatus生成以下内容(如上所述),这会扰乱我的服务器


解决方法是在创建父元素之后,在赋值之前,将所有
Meta\u SubG\u 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...
这导致sud生成没有空元素的SOAP/XML,这是我的服务器可以接受的

但是有人知道一种更干净的方法来达到同样的效果吗

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

此解决方案使用一个Suds MessagePlugin在Suds将请求发送到网络之前删除表单
的“空”XML。我们只需要删减Shupdate(我们正在更新服务器上的数据),逻辑(尤其是向下索引到子元素以获取服务指示元素列表)非常特定于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)

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

假设XML数据位于字符串
xmltext

import re
filteredtext = re.sub('\s+<.*?/>', '', xmltext)
重新导入
filteredtext=re.sub('\s+','',xmltext)

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


我想我应该分享一个关于上述解决方案的非常简单的更新,它应该适用于任何WSDL: 请注意,sending方法不是必需的,这样您就可以在marshal方法运行之前启动客户机的调试请求打印,从而审核您的更改

class XMLBS_Plugin(MessagePlugin):
def marshalled(self, context):
    def w(x):
        if x.isempty():
            print "EMPTY: ", x
            x.detach()

    context.envelope.walk(w)

def sending(self,context):
    c = copy.deepcopy(context.envelope)
    c=c.replace('><','>\n<') # some sort of readability
    logging.info("SENDING: \n%s"%c)
classxmlbs_插件(MessagePlugin):
已封送的def(自身、上下文):
def w(x):
如果x.isempty():
打印“空:”,x
x、 分离()
context.envelope.walk(w)
def发送(自身、上下文):
c=copy.deepcopy(context.envelope)

c=c.replace('>\n您认为下面的MonkeyPatch如何使用
None
值跳过复杂类型

from suds.mx.literal import Typed
old_skip = Typed.skip
def new_skip(self, content):
    x = old_skip(self, content)
    if not x and getattr(content.value, 'value', False) is None:
        x = True
    return x
Typed.skip = new_skip

我知道这个问题很久以前就被关闭了,但在亲自解决这个问题之后,我发现了目前的答案
from suds.mx.literal import Typed
old_skip = Typed.skip
def new_skip(self, content):
    x = old_skip(self, content)
    if not x and getattr(content.value, 'value', False) is None:
        x = True
    return x
Typed.skip = new_skip
class ClearEmpty(MessagePlugin):
    def clear_empty_tags(self, tags):
        for tag in tags:
            children = tag.getChildren()[:]
            if children:
                self.clear_empty_tags(children)
            if re.match(r'^<[^>]+?/>$', tag.plain()):
                tag.parent.remove(tag)

    def marshalled(self, context):
        self.clear_empty_tags(context.envelope.getChildren()[:])
cvc-enumeration-valid: Value '' is not facet-valid with respect to enumeration
class PrunePlugin(MessagePlugin):
    def marshalled(self, context):
        context.envelope = context.envelope.prune()
client = Client(url, plugins=[PrunePlugin()])
>>> order_details = c.factory.create('ns2:OrderDetails')
>>> order_details
(OrderDetails){
   Amount = None
   CurrencyCode = None
   OrderChannelType =
      (OrderChannelType){
         value = None
      }
   OrderDeliveryType =
      (OrderDeliveryType){
         value = None
      }
   OrderLines =
      (ArrayOfOrderLine){
         OrderLine[] = <empty>
      }
   OrderNo = None
   TotalOrderValue = None
 }
>>> del order_details.OrderLines
>>> del order_details.OrderDeliveryType
>>> del order_details.OrderChannelType
>>> order_details
(OrderDetails){
   Amount = None
   CurrencyCode = None
   OrderNo = None
   TotalOrderValue = None
 }