【问题标题】:Suds write request missing datatype?Suds 写入请求缺少数据类型?
【发布时间】:2012-11-27 22:02:32
【问题描述】:

我正在尝试使用 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中的医生=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 的一些基本理解......

【问题讨论】:

    标签: python soap suds


    【解决方案1】:

    我使用Adding xsi:type and envelope namespace when using SUDS (https://stackoverflow.com/a/10977734/696768) 的答案设法解决了这个问题

    我不确定这是唯一可能的解决方案,对我来说,这似乎更像是一种 hack,但它适用于我目前的情况。

    我使用的解决方案是为客户端制作一个插件,寻找我需要成为 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])
    

    【讨论】:

    • 谢谢。看起来这就是这些天的“方法”。我希望有更好的方法。在 suds 图书馆里花了几个小时试图弄清楚这一点,但气喘吁吁。仍在尝试决定是否对此进行投票。
    • 嗯,赞成,但只是因为它很有帮助。我正在等待“真正”的解决方案。
    【解决方案2】:

    这不是一个“答案”,因为这个问题是客户端的。但我现在把它放在这里是为了搜索引擎。

    问题是请求消息是一个复杂的类型。

    我的解决方案是在服务器端。我的服务现在接受请求中的无类型元素。 请求正文的服务器端解析必须知道请求模式。一旦发生这种情况,服务器就可以对请求进行类型检查和解析,而无需客户端键入元素。

    具体来说,我的错误来自使用 Python ZSI 模块和 Zope 实现的服务。

    Any 无法解析无类型元素

    在这里,我得到了有关复杂请求对象的提示:
    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 满意,您只需创建一个表示请求消息的类,并为其添加一个类型代码。这就是为什么你会看到很多“操作 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")
    

    以此类推,直到您一直深入了解所有原始类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多