如何强制ElementTree将xmlns属性保留在其原始元素中?

时间:2016-02-23 10:24:29

标签: python xml elementtree

我有一个输入XML文件:

<?xml version='1.0' encoding='utf-8'?>
<configuration>
  <runtime name="test" version="1.2" xmlns:ns0="urn:schemas-microsoft-com:asm.v1">
    <ns0:assemblyBinding>
      <ns0:dependentAssembly />
    </ns0:assemblyBinding>
  </runtime>
</configuration>

...和Python脚本:

import xml.etree.ElementTree as ET

file_xml = 'test.xml'

tree = ET.parse(file_xml)
root = tree.getroot()
print (root.tag)
print (root.attrib)

element_runtime = root.find('.//runtime')
print (element_runtime.tag)
print (element_runtime.attrib)

tree.write(file_xml, xml_declaration=True, encoding='utf-8', method="xml")

...提供以下输出:

>test.py
configuration
{}
runtime
{'name': 'test', 'version': '1.2'}

...并且有一个不良的副作用,即将XML修改为:

<?xml version='1.0' encoding='utf-8'?>
<configuration xmlns:ns0="urn:schemas-microsoft-com:asm.v1">
  <runtime name="test" version="1.2">
    <ns0:assemblyBinding>
      <ns0:dependentAssembly />
    </ns0:assemblyBinding>
  </runtime>
</configuration> 

我的原始脚本修改了XML,因此我必须调用tree.write并保存已编辑的文件。但问题是ElementTree解析器将xmlns属性从runtime元素移动到根元素configuration,这在我的情况下是不可取的。

我无法从根元素中删除xmlns属性(将其从其属性的字典中删除),因为它未在其属性列表中列出(与runtime列出的属性不同元件)。

为什么xmlns属性永远不会列在任何元素的属性列表中?

如何强制ElementTree将xmlns属性保留在其原始元素中?

我在Windows上使用Python 3.5.1。

2 个答案:

答案 0 :(得分:2)

xml.etree.ElementTree将所有名称空间拉入第一个元素,因为它内部没有跟踪最初声明名称空间的元素。

如果您不想要,那么您必须编写自己的序列化逻辑。

更好的选择是使用lxml而不是xml.etree,因为它会保留声明命名空间前缀的位置。

答案 1 :(得分:1)

按照@mata 的建议,我在这里给出了一个带有代码和 xml 文件的示例的答案。

xml输入如图(原始和修改) enter image description here

python 代码检查 NtnlCcy 名称,如果它是“EUR”,则将价格转换为美元(乘以 EURUSD:= 1.2)并将 NtnlCcy 名称更改为“USD”。

python代码如下:

from lxml import etree
pathToXMLfile = r"C:\Xiang\codes\Python\afmreports\test_original.xml"
tree = etree.parse(pathToXMLfile)
root = tree.getroot()
EURUSD = 1.2

for Rchild in root: 
    print ("Root child: ", Rchild.tag, ". \n")

    if Rchild.tag.endswith("Pyld"):
        for PyldChild in Rchild: 
            print ("Pyld Child: ", PyldChild.tag, ". \n")
        Doc = Rchild.find('{001.003}Document')
        FinInstrNodes = Doc.findall('{001.003}FinInstr')
    
        for FinInstrNode in FinInstrNodes:
            FinCcyNode = FinInstrNode.find('{001.003}NtnlCcy')
            FinPriceNode = FinInstrNode.find('{001.003}Price')
        
            FinCcyNodeText = ""
            if FinCcyNode is not None: 
                CcyNodeText = FinCcyNode.text
            if CcyNodeText == "EUR":
                PriceText = FinPriceNode.text
                Price = float(PriceText)
                FinPriceNode.text = str(Price * EURUSD) 
                FinCcyNode.text = "USD"

tree.write(r"C:\Xiang\codes\Python\afmreports\test_modified.xml", encoding="utf-8", xml_declaration=True) 
print("\n the program runs to the end! \n")  

当我们比较原始和修改后的 xml 文件时,命名空间保持不变,xml 的整个结构保持不变,只是根据需要更改了一些 NtnlCcy 和价格节点。

我们不想要的唯一细微差别是第一行。在原始xml文件中,它是<?xml version="1.0" encoding="UTF-8"?>,而在修改后的xml文件中,它是<?xml version='1.0' encoding='UTF-8'?>。引号从双引号变为单引号。但我们认为这个微小的差异应该无关紧要。

将附加原始文件上下文以供您轻松测试:

<?xml version="1.0" encoding="UTF-8"?>
<BizData xmlns="001.001">
<Hdr>
    <AppHdr xmlns="001.002">
        <Fr>
            <Id>XXX01</Id>
        </Fr>
        <To>
            <Id>XXX02</Id>
        </To>
        <CreDt>2019-10-25T15:38:30</CreDt>
    </AppHdr>
</Hdr>
<Pyld>
    <Document xmlns="001.003">
        <FinInstr>
            <Id>NLENX240</Id>
            <FullNm>AO.AAI</FullNm>
            <NtnlCcy>EUR</NtnlCcy>
            <Price>9</Price>
        </FinInstr>
        <FinInstr>
            <Id>NLENX681</Id>
            <FullNm>AO.ABN</FullNm>
            <NtnlCcy>USD</NtnlCcy>
            <Price>10</Price>
        </FinInstr>
        <FinInstr>
            <Id>NLENX320</Id>
            <FullNm>AO.ING</FullNm>
            <NtnlCcy>EUR</NtnlCcy>
            <Price>11</Price>
        </FinInstr>
    </Document>
</Pyld>