SUDS不将响应解析为数组(只返回第一个条目)

0 投票
1 回答
1217 浏览
提问于 2025-04-18 10:14

使用SUDS库向SOAP网络服务发送请求时,应该返回一个数组,里面包含多个EnvelopeType实例(别问我这个类型名字怎么来的)。但是现在只解析并返回了数组中的第一个条目。响应的wsdl:type定义如下:

<wsdl:types>
  <xs:element name="ShipmentsSearchDFURes">
    <xs:annotation>
      <xs:documentation>Comment describing your root element</xs:documentation>
    </xs:annotation>
    <xs:complexType>
      <xs:sequence minOccurs="0" maxOccurs="unbounded">
        <xs:element name="ZEnvelope" type="dfu:EnvelopeType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</wsdl:types>

然后,使用client.last_received()获得的原始XML响应是:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope>
   <soap:Body>
      <ns11:ShipmentsSearchDFURes>
         <Envelope Version="2.2">
            <FileInfos FileID="1" FileTime="16:10:13.110+02:00" FileDate="2014-06-18+02:00" />
            <Data>
              <!-- ... Omitted for brevity ... -->
            </Data>
         </Envelope>
         <Envelope Version="2.2">
            <FileInfos FileID="2" FileTime="16:12:13.117+02:00" FileDate="2014-06-18+02:00"/>
            <Data>
              <!--  ... -->
            </Data>
         </Envelope>
      </ns11:ShipmentsSearchDFURes>
   </soap:Body>
</soap:Envelope>

我并不是SOAP方面的专家,但我本来期待调用

client.service.ShipmentsSearch(...)

会返回一个数组,里面有多个EnvelopeType实例,但现在只返回了第一个条目,也就是FileID="1"的那个。也就是说,应该返回的内容是:

[envelope_1, envelope_2]

但我只得到了envelope_1。非常感谢任何帮助。

注意 #1:我只是这个WebService的使用者。

注意 #2:PHP的SOAP客户端确实返回了一个包含多个EnvelopeType实例的数组。是不是SUDS出了什么问题?或者是WSDL有问题?

1 个回答

0

部分解析的问题可以部分归咎于错误的类型定义,因为

  <xs:sequence minOccurs="0" maxOccurs="unbounded">
    <xs:element name="ZEnvelope" type="dfu:EnvelopeType"/>
  </xs:sequence>

应该改成:

  <xs:sequence>
    <xs:element name="ZEnvelope" type="dfu:EnvelopeType" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>

根据W3.org上的XML Schema。使用本地的WSDL文件和里面引用的文件,并做上述修改,可以解决这个问题。


如果你想知道,当解析原始的XML响应时,SUDS会使用XSD模式,而导致解析不好的根本原因是因为在Binding.get_reply()方法的第158到161行对根节点的处理有点过于严格:

130     def get_reply(self, method, reply):
131         """
132         Process the I{reply} for the specified I{method} by sax parsing the I{reply}
133         and then unmarshalling into python object(s).
134         @param method: The name of the invoked method.
135         @type method: str
136         @param reply: The reply XML received after invoking the specified method.
137         @type reply: str
138         @return: The unmarshalled reply.  The returned value is an L{Object} for a
139             I{list} depending on whether the service returns a single object or a
140             collection.
141         @rtype: tuple ( L{Element}, L{Object} )
142         """
143         reply = self.replyfilter(reply)
144         sax = Parser()
145         replyroot = sax.parse(string=reply)
146         plugins = PluginContainer(self.options().plugins)
147         plugins.message.parsed(reply=replyroot)
148         soapenv = replyroot.getChild('Envelope')
149         soapenv.promotePrefixes()
150         soapbody = soapenv.getChild('Body')
151         self.detect_fault(soapbody)
152         soapbody = self.multiref.process(soapbody)
153         nodes = self.replycontent(method, soapbody)
154         rtypes = self.returned_types(method)
155         if len(rtypes) > 1:
156             result = self.replycomposite(rtypes, nodes)
157             return (replyroot, result)
158         if len(rtypes) == 1:
159             if rtypes[0].unbounded():
160                 result = self.replylist(rtypes[0], nodes)
161                 return (replyroot, result)
162             if len(nodes):
163                 unmarshaller = self.unmarshaller()
164                 resolved = rtypes[0].resolve(nobuiltin=True)
165                 result = unmarshaller.process(nodes[0], resolved)
166                 return (replyroot, result)
167         return (replyroot, None)

在这里,作者完全依赖于类型定义,而不考虑nodes列表的大小。具体来说,我认为第159行的测试应该改成:

158         if len(rtypes) == 1:
159             if rtypes[0].unbounded() or len(nodes)>1:
160                 result = self.replylist(rtypes[0], nodes)
161                 return (replyroot, result)

也许还应该加个警告,提示XSD和实际响应之间的不匹配。希望这对你有帮助。

撰写回答