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

How to force ElementTree to keep xmlns attribute within its original element?

我有一个输入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 属性(将其从其属性字典中删除),因为它未在其属性列表中列出(与 [=16 列出的属性不同) =] 元素)。

为什么 xmlns 属性从未在任何元素的属性列表中列出?

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

我在 Windows 上使用 Python 3.5.1。

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

如果您不想这样,则必须编写自己的序列化逻辑。

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

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

xml输入如图(原始和修改)

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>