2012-11-27 62 views
0

我想通过使用Suds的web服务进行通信,从服务中读取工作正常,但是写入会引发错误。Suds写请求缺少数据类型?

suds.WebFault:服务器提出故障:“而试图反序列化消息格式化抛出一个异常 :有,而 尝试反序列化参数http://tempuri.org/:tagValues错误。 InnerException消息是'来自命名空间的元素值 http://schemas.datacontract.org/2004/07/NOV.Api.Messages不能有 子内容被反序列化为一个对象。请使用XmlNode [] 来反序列化这种XML模式。'。有关 的更多详细信息,请参阅InnerException。

的XML产生似乎不添加neccessary的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:类型

我加

schema_url = 'http://schemas.xmlsoap.org/soap/encoding/' 
schema_import = Import(schema_url) 
schema_doctor = ImportDoctor(schema_import) 

和医生= 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:

suds.WebFault:服务器提出故障:

<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> 

试图发送它,当我现在得到了以下异常“格式化扔尝试反序列化消息时出现异常 :尝试反序列化参数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的已知类型列表中' '。请参阅 以了解更多细节。'

看起来稍微接近一点,但好像有一些命名空间混乱?

完整的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) 

创建客户端似乎有在这里是对计算器几个类似的问题,他们大多提ImportDoctor以类似的方式,我试过。我缺少一些SOAP我怀疑的基本理解...

回答

2

我设法解决它,使用从Adding xsi:type and envelope namespace when using SUDShttps://stackoverflow.com/a/10977734/696768

我不知道这是唯一可能的解决办法,并我认为它比其他任何东西都更像是一种黑客攻击,但是它对我目前的情况来说可以正常工作。

我使用的解决方案是为客户端制作插件,寻找我需要的特定元素xsi:type =“xsd:int”,然后将这些属性添加到这些元素。

代码我结束了使用用于参考(从与微小的调整前述计算器问题):

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() 

然后,我添加插件= [插件]给客户端构造函数。

实施例:

client = suds.client.Client("http://127.0.0.1:8099/TagValueWriteService?wsdl",plugins=[plugin]) 
+0

谢谢。看起来这是这些日子里'这样做'的方法。我希望有更好的方法。 在泡沫图书馆里花了几个小时的时间试图找出这个问题,但却喘不过气来。仍在尝试决定是否注册这个或没有。 – FlipMcF

+0

呃,upvoted,但只是因为它是有帮助的。我正在等待'REAL'解决方案。 – FlipMcF

1

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

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

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

具体来说,我的错误来自于一个由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开心,您只需创建一个代表请求消息的类,然后为其添加一个类型代码。这就是为什么你看到很多“运行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") 

依此类推,直到你钻完所有原始类型为止。