使用Python中的ElementTree解析XML中的xsi:type

时间:2019-06-04 09:25:01

标签: python xml api soap elementtree

我正在尝试连接到RESTful API,并且在构建XML请求时遇到了问题,因为我正在使用Elementree库。

我有一个必须发送请求的XML示例。从该示例中构建一个模型,然后通过代码编写不同的属性。但是输出的XML与我给出的示例不完全相同,并且无法连接到API。

这是我的示例:

  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
      <GetLoc xmlns="http://abc/Getloc">
        <request>
          <Access>
            <string xmlns="http://bcd/Arrays"></string>
          </Access>
          <Details xsi:type="Request">
            <Postcode ></Postcode >
          </Details>
          <UserConsent>Yes</UserConsent>
        </request>
      </GetLoc>
    </soap:Body>
  </soap:Envelope>

这是我的代码:

tree = ET.parse('model.xml')
root = tree.getroot()
ns = {'loc':'http://abc/Getloc',\
        'arr':http://bcd/Arrays',\
        'soapenv':'http://schemas.xmlsoap.org/soap/envelope/', \
        'xsi':"http://www.w3.org/2001/XMLSchema-instance", \
         xsd': "http://www.w3.org/2001/XMLSchema"}

tree.find('.//arr:string', ns).text = 'THC'
tree.find('.//Postcode ', ns).text = '15478'

这是输出XML(SOAP):

  <ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://abc/Getloc" xmlns:ns2="http://bcd/Arrays" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ns0:Body>
      <ns1:GetLoc >
        <ns1:request>
          <ns1:Access>
            <ns2:string>THC</ns2:string>
          </ns1:Access>
          <ns1:Details xsi:type="Request">
            <ns1:Postcode >15478</ns1:Postcode >
          </ns1:Details>
          <ns1:UserConsent>Yes</ns1:UserConsent>
        </ns1:request>
      </ns1:GetLoc >
    </ns0:Body>
  </ns0:Envelope>

使用示例(上面的第一个示例),连接到API时我没有问题。但是,第二个错误却提示:

 " status="Service Not Found.  The request may have been sent to an invalid URL, or intended for an unsupported operation." xmlns:l7="http://www.layer7tech.com/ws/policy/fault"/>"

两个XML都使用相同的标头和auth发送到相同的URL。我看到两个XML都等效,所以我期待相同的行为。我不明白为什么它不起作用。

编辑:输出XML必须像

<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://abc/Getloc" xmlns:ns2="http://bcd/Arrays" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <ns0:Body>
          <ns1:GetLoc >
            <ns1:request>
              <ns1:Access>
                <ns2:string>THC</ns2:string>
              </ns1:Access>
              <ns1:Details xsi:type="ns1:Request">
                <ns1:Postcode >15478</ns1:Postcode >
              </ns1:Details>
              <ns1:UserConsent>Yes</ns1:UserConsent>
            </ns1:request>
          </ns1:GetLoc >
        </ns0:Body>
      </ns0:Envelope>

但是我不知道要更改代码以获取: xsi:type =“ ns1:Request”

1 个答案:

答案 0 :(得分:0)

最后,我自己找到了解决方案。

解决方案在here(一篇非常完整的文章)中,因为我已经在使用ElementTree。您可能会找到其他解决方案,例如使用lxml库。

因此,对于ElementTree,我只需要使用自己的解析器而不是标准的 ElementTree.parse('file.xml')

xsi属性名称由解析器处理,但是解析器不知道该属性也恰好包含限定名称,因此它保持原样。为了能够处理这种格式,可以使用自定义解析器,该解析器知道如何处理某些属性和元素,或者跟踪每个元素的前缀映射。 为此,您可以使用iterparse解析器,并要求它报告“ start-ns”和“ end-ns”事件。以下代码段将ns_map属性添加到每个元素,其中包含适用于该特定元素的前缀/ URI映射:

def parse_map(file):
    events = "start", "start-ns", "end-ns"
    root = None
    ns_map = []
    for event, elem in ET.iterparse(file, events):
        if event == "start-ns":
            ns_map.append(elem)
        elif event == "end-ns":
            ns_map.pop()
        elif event == "start":
            if root is None:
                root = elem
            elem.ns_map = dict(ns_map)
    return ET.ElementTree(root)