我正在尝试使用Suds与Web服务进行通信,从服务中读取工作正常,但写入会引发错误。
suds.WebFault:服务器引发的错误:'格式化程序引发异常 在尝试反序列化消息时:出现错误 尝试反序列化参数http://tempuri.org/:tagValues。该 InnerException消息是'来自命名空间的元素值 http://schemas.datacontract.org/2004/07/NOV.Api.Messages不能拥有 将要反序列化的子内容作为对象。请使用XmlNode [] 反序列化这种XML模式。'请参阅InnerException 更多细节。'
XML生成似乎没有添加必要的xsi:type =“xsd:int”
制作人:
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value>23</ns1:Value>
</ns1:TagValue>
预期:
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value xsi:type="xsd:int">23</ns1:Value>
</ns1:TagValue>
在搜索之后,我想尝试使用ImportDoctor查看我是否可以进入xsi:type
我添加了
schema_url = 'http://schemas.xmlsoap.org/soap/encoding/'
schema_import = Import(schema_url)
schema_doctor = ImportDoctor(schema_import)
客户端ctor中的和doctor = schema_doctor
现在这给了我一个额外的前缀和一个扩展的类型列表
Prefixes (4)
ns0 = "http://schemas.datacontract.org/2004/07/NOV.Api.Messages"
ns1 = "http://schemas.microsoft.com/2003/10/Serialization/"
ns2 = "http://schemas.xmlsoap.org/soap/encoding/"
ns3 = "http://tempuri.org/"
我现在有一个ns2:int
我使用工厂创建了一个ns2:int类型的对象,将其值设置为23
发送时,我得到以下XML:
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value xsi:type="ns2:int">23</ns1:Value>
</ns1:TagValue>
我在尝试发送时遇到以下异常:
suds.WebFault:服务器引发的错误:'格式化程序引发异常 在尝试反序列化消息时:出现错误 尝试反序列化参数http://tempuri.org/:tagValues。该 InnerException消息是'第1行位置651中的错误。元素 'http://schemas.datacontract.org/2004/07/NOV.Api.Messages:Value' 包含映射到名称'http://schemas.xm的类型的数据 lsoap.org/soap/encoding/:int”。解串器不知道 任何映射到此名称的类型。考虑使用DataContractResolver 或者将'int'对应的类型添加到已知类型列表中 - 例如,通过使用KnownTypeAttribute属性或添加它 到传递给DataContractSerializer的已知类型列表。'。请 有关详细信息,请参阅InnerException。'
似乎距离稍微近似,但似乎有一些混乱的命名空间?
制作完整的XML:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns3="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns0="http://tempuri.org/" xmlns:ns1="http://schemas.datacontract.org/2004/07/NOV.Api.Messages" xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns3:Body>
<ns0:WriteRealtimeValues>
<ns0:tagValues>
<ns1:TagValue>
<ns1:Quality>
<ns1:Id>1</ns1:Id>
<ns1:QualityData>Quality</ns1:QualityData>
</ns1:Quality>
<ns1:TagID>
<ns1:Id>0</ns1:Id>
<ns1:TagID>BitDepth</ns1:TagID>
</ns1:TagID>
<ns1:Value xsi:type="ns2:int">23</ns1:Value>
</ns1:TagValue>
</ns0:tagValues>
</ns0:WriteRealtimeValues>
</ns3:Body>
</SOAP-ENV:Envelope>
作为参考,我使用以下代码
创建客户端credentials = dict(username='%s' % (username), password='%s' % password)
url= "http://%s:%s/TagValueWriteService?wsdl" % (ip,port)
self.transport = HttpAuthenticated(**credentials)
suds.client.Client.__init__(self,url, transport=self.transport, cache=None,doctor=schema_doctor)
在stackoverflow上似乎有几个类似的问题,大多数人以与我尝试过的类似方式提到ImportDoctor。我缺乏对SOAP怀疑的一些基本理解......
答案 0 :(得分:2)
我设法使用Adding xsi:type and envelope namespace when using SUDS(https://stackoverflow.com/a/10977734/696768)
的答案解决了这个问题我不确定这是唯一可行的解决方案,对我而言,它似乎更像是一个黑客而不是其他任何东西,但它对我当前的情况会很好。
我使用的解决方案是为客户端创建一个插件,寻找我需要的特定元素xsi:type =“xsd:int”,然后将这些属性添加到这些元素中。
我最终使用的代码用于参考(来自上述的stackoverflow问题并进行了微调):
from suds.plugin import MessagePlugin
from suds.sax.attribute import Attribute
class SoapFixer(MessagePlugin):
def marshalled(self, context):
# Alter the envelope so that the xsd namespace is allowed
context.envelope.nsprefixes['xsd'] = 'http://www.w3.org/2001/XMLSchema'
# Go through every node in the document and apply the fix function to patch up incompatible XML.
context.envelope.walk(self.fix_any_type_string)
def fix_any_type_string(self, element):
"""Used as a filter function with walk in order to fix errors.
If the element has a certain name, give it a xsi:type=xsd:int. Note that the nsprefix xsd must also
be added in to make this work."""
# Fix elements which have these names
fix_names = ['Value', 'anotherelementname']
if element.name in fix_names:
element.attributes.append(Attribute('xsi:type', 'xsd:int'))
plugin=SoapFixer()
然后我将plugins = [plugin]添加到客户端ctor。
示例:
client = suds.client.Client("http://127.0.0.1:8099/TagValueWriteService?wsdl",plugins=[plugin])
答案 1 :(得分:1)
这不是'答案',因为这个问题是客户端的。但是我现在把它放在这里搜索引擎。
问题是请求消息是一种复杂的类型。
我的解决方案是在服务器端。我的服务现在接受请求中的无类型元素。 请求主体的服务器端解析必须知道Request模式。一旦发生这种情况,服务器可以检查并解析请求,而不会由客户端键入元素。
具体来说,我的错误来自使用Python ZSI模块和Zope实现的服务。
任何无法解析无类型元素
在这里,我得到了复杂请求对象的提示:
http://pypi.python.org/pypi/z3c.soap/(请参阅ValidateEmailRequest)
在这里,我参加了ZSI的速成课程: Are there any working examples of Zolera SOAP Infrastructure (ZSI)?
体面的ZSI文档:http://pywebsvcs.sourceforge.net/zsi.html#SECTION0071100000000000000000
要使ZSI满意,您只需创建一个表示Request消息的类,并为其添加一个类型代码。这就是为什么你看到很多服务“operation foo”和“fooRequest”和“fooResponse”,所以他们可以将请求和响应对象键入为xml复杂类型。
对于上面的例子,我会将这样的东西导入到解析soap请求体的命名空间中。你可以变得更加复杂,但这确实是必要的:
import ZSI
class WriteRealTimeValuesRequest(object):
tagValues = array() #of TagValue
WriteRealTimeValuesRequest.typecode = ZSI.TC.Struct(WriteRealTimeValuesRequest,
(ZSI.TC.Array("TagValue",
TagValue.typecode,
"tagValues"
),
),
"WriteRealTimeValuesRequest")
“什么是标签值?”
class TagValue(object):
Quality = Quality
TagId = TagId
Value = Value
TagValue.typecode = ZSI.TC.Struct(TagValue,
(Quality.typecode,
TagId.typecode,
Value.typecode),
"TagValue")
什么是质量?
class Quality(object):
Id = 0
QualityData = "I'm a string"
Quality.typecode = ZSI.TC.Struct(Quality,
(ZSI.TC.Integer("Id"), #this is the secret sauce
ZSI.TC.String("QualityData") #and here
),
"Quality")
依此类推,直到你一直钻到所有原始类型。