[主要编辑基于两天前第一篇文章以来的经验.]
我正在使用Suds构建Python SOAP/XML脚本,但我很难获得生成服务器可接受的SOAP/XML的代码.我曾经认为问题是Suds没有为内部元素生成前缀,但后来发现缺少前缀(参见Sh-Data和内部元素)不是问题,因为Sh-Data和MetaSwitchData元素声明了适当的命名空间(见下文).
<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>
Run Code Online (Sandbox Code Playgroud)
但这仍然失败.问题是Suds为可选元素生成空元素(Mandatory = No在WSDL中标记).但是服务器要求可选元素存在具有合理值或不存在,并且我得到以下错误(因为该<CallFeatureServerControlStatus/>元素不是允许值之一.
提供的用户数据未针对用户数据针对MetaSwitch XML架构进行验证.
详细信息:cvc-enumeration-valid:对于枚举'[Controlling,Abandoned,Cautiously controlling]',值''不是facet-valid.它必须是枚举中的值.
如果我将生成的SOAP/XML带入SOAPUI并删除空元素,那么请求就可以了.
有没有办法让Suds不为可选字段生成空元素,或者让我之后在代码中删除它们?
主要更新
我已经解决了这个问题(我在其他地方已经看过)但是以一种非常不优雅的方式.所以我发布我当前的解决方案,希望a)它可以帮助他人和/或b)有人可以建议更好的解决方案.
事实证明,问题不在于Suds为可选元素生成空元素(Mandatory = No在WSDL中标记).而是说Suds为可选的复杂元素生成空元素.例如,以下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>
Run Code Online (Sandbox Code Playgroud)
相比之下,下面的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>
Run Code Online (Sandbox Code Playgroud)
Suds为ProviderStatus生成以下内容(如上所述)扰乱了我的服务器.
<ProviderStatus/>
Run Code Online (Sandbox Code Playgroud)
解决方法是在创建父元素之后以及在分配值之前将所有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...
Run Code Online (Sandbox Code Playgroud)
这导致Suds生成没有空元素的SOAP/XML,这是我的服务器可以接受的.
但有没有人知道更清洁的方法来达到同样的效果?
以下解决方案基于dusan和Roland Smith的回答/评论.
<SubscriberType/>在Suds将请求发送到线路之前,此解决方案使用Suds MessagePlugin来修剪表单的"空"XML .我们只需要修剪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)
Run Code Online (Sandbox Code Playgroud)
然后我们只需要在创建客户端时引用该插件.
client = Client(url, plugins=[MyPlugin()])
Run Code Online (Sandbox Code Playgroud)
dus*_*san 15
您可以使用插件在发送到服务器之前修改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()])
Run Code Online (Sandbox Code Playgroud)
引用文档:
MessagePlugin当前有(5)hooks ::
(...)
sending()
为插件提供在发送之前检查/修改消息文本的机会.
基本上,Suds会sending在发送XML之前调用,因此您可以修改生成的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()
Run Code Online (Sandbox Code Playgroud)
有一种更简单的方法 - 不需要任何Reg Ex或激动人心的迭代器;)
首先,定义插件:
class PrunePlugin(MessagePlugin):
def marshalled(self, context):
context.envelope = context.envelope.prune()
Run Code Online (Sandbox Code Playgroud)
然后在创建客户端时使用它:
client = Client(url, plugins=[PrunePlugin()])
Run Code Online (Sandbox Code Playgroud)
prune()方法将删除所有空节点,如下所述:http://jortel.fedorapeople.org/suds/doc/suds.sax.element.Element-class.html
Suds 工厂方法生成一个常规 Python 对象,该对象具有映射到 WSDL 类型定义的常规 Python 属性。
您可以使用 'del' 内置函数来删除属性。
>>> 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
}
Run Code Online (Sandbox Code Playgroud)
您可以使用正则表达式过滤掉空元素。
假设您的 XML 数据位于字符串中xmltext;
import re
filteredtext = re.sub('\s+<.*?/>', '', xmltext)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6595 次 |
| 最近记录: |