尝试使用python在XML中插入元素,但不是将其添加为单独的元素,而是将代码添加为SubElement

问题描述

我有以下示例XML文件,我想在<item>DEV</item>之后添加元素<item>Production</item>

之前(Sample.xml)

<resource xmlns:ns0="http://schemas.android.com/apk" xmlns:ns1="http://schemas.android">
    <string-array name="selectable_environment" ns1:ignore="InconsistenArray">
        <item>QA</item>
        <item>SIT</item>
        <item>Staging</item>
        <item>Production</item>
        <item>RQA</item>
        <item>PTE</item>
    </string-array>
<resource>

预期输出(Output.xml)

<resource xmlns:ns0="http://schemas.android.com/apk" xmlns:ns1="http://schemas.android">
    <string-array name="selectable_environment" ns1:ignore="InconsistenArray">
        <item>QA</item>
        <item>SIT</item>
        <item>Staging</item>
        <item>Production</item>
        <item>DEV</item>
        <item>RQA</item>
        <item>PTE</item>
    </string-array>
<resource>

这是我的示例代码

import xml.etree.ElementTree as ET
mytree = ET.parse("Sample.xml")
root = mytree.getroot()
for child in root.iter('item'):
    if child.text == "Production":
        new = ET.SubElement(child,'item')
        new.text = "DEV"
mytee.write("Output.xml")

我得到的结果是

<resource xmlns:ns0="http://schemas.android.com/apk" xmlns:ns1="http://schemas.android">
    <string-array name="selectable_environment" ns1:ignore="InconsistenArray">
        <item>QA</item>
        <item>SIT</item>
        <item>Staging</item>
        <item>Production<item>DEV</item></item>
        <item>DEV</item>
        <item>RQA</item>
        <item>PTE</item>
    </string-array>
<resource>

解决方法

要解决您的问题,您可以使用文本“生产”来收集节点的父级。您还需要使节点的索引带有文本“ Production”:您可以使用函数enumerate()来获得它。您可以这样编码:

import xml.etree.ElementTree as ET
mytree = ET.parse("Sample.xml")
root = mytree.getroot()
for child in root.iter():
    for i,elt in enumerate(child.getchildren()):
        if elt.text == "Production":
            new = ET.Element('item')
            new.text = "DEV"
            child.insert(i+1,new)
mytree.write("Output.xml")
,

能否请您分享一些有关如何使用lxml添加新元素的示例。我尝试使用@ Pierre-Loic shared解决方案,它可以正常工作,但是在添加新Element之后,它不会创建新行。

这是在lxml中使用addnext()方法的核心示例...

from lxml import etree

tree = etree.parse("input.xml")

try:
    target_elem = tree.xpath("./string-array/item[.='Production']")[0]
    new_elem = etree.fromstring("<item>DEV</item>")  # Could also construct a new Element and set .text.
    target_elem.addnext(new_elem)
except IndexError:
    print("Unable to find target element.")

tree.write("output.xml")

但是,正如您将看到的,这还会在同级的同一行上输出新的item ...

<resource xmlns:ns0="http://schemas.android.com/apk" xmlns:ns1="http://schemas.android">
   <string-array name="selectable_environment" ns1:ignore="InconsistenArray">
       <item>QA</item>
       <item>SIT</item>
       <item>Staging</item>
       <item>Production</item><item>DEV</item>
       <item>RQA</item>
       <item>PTE</item>
   </string-array>
</resource>

这纯粹是格式化,但是如果您想“重置”漂亮的打印,我建议将树序列化为字符串,然后使用XMLParser解析该字符串以去除所有空白,然后将新的漂亮打印的树写入一个文件...

from lxml import etree

tree = etree.parse("input.xml")

try:
    target_elem = tree.xpath("./string-array/item[.='Production']")[0]
    new_elem = etree.fromstring("<item>DEV</item>")  # Could also construct a new Element and set .text.
    target_elem.addnext(new_elem)
except IndexError:
    print("Unable to find target element.")

# "Reset" pretty printing and write to file.
parser = etree.XMLParser(remove_blank_text=True)
etree.ElementTree(etree.fromstring(etree.tostring(tree),parser=parser)).write("output.xml",pretty_print=True)

输出...

<resource xmlns:ns0="http://schemas.android.com/apk" xmlns:ns1="http://schemas.android">
  <string-array name="selectable_environment" ns1:ignore="InconsistenArray">
    <item>QA</item>
    <item>SIT</item>
    <item>Staging</item>
    <item>Production</item>
    <item>DEV</item>
    <item>RQA</item>
    <item>PTE</item>
  </string-array>
</resource>

我们可以在元素中添加缩进吗?要求是<item>...</item>带有4个缩进。现在我看到所有元素只有2个。原始文件有4个,但是此脚本在添加了新的Element之后将缩进更改为2。

如果您使用的是lxml 4.5或更高版本,则可以使用indent() function ...

from lxml import etree

tree = etree.parse("input.xml")

try:
    target_elem = tree.xpath("./string-array/item[.='Production']")[0]
    new_elem = etree.fromstring("<item>DEV</item>")  # Could also construct a new Element and set .text.
    target_elem.addnext(new_elem)
except IndexError:
    print("Unable to find target element.")

# "Reset" pretty printing.
parser = etree.XMLParser(remove_blank_text=True)
new_tree = etree.ElementTree(etree.fromstring(etree.tostring(tree),parser=parser))
# Indent 4 spaces instead of the default 2.
etree.indent(new_tree,space="    ")
# Write to file.
new_tree.write("output.xml",pretty_print=True)

输出...

<resource xmlns:ns0="http://schemas.android.com/apk" xmlns:ns1="http://schemas.android">
    <string-array name="selectable_environment" ns1:ignore="InconsistenArray">
        <item>QA</item>
        <item>SIT</item>
        <item>Staging</item>
        <item>Production</item>
        <item>DEV</item>
        <item>RQA</item>
        <item>PTE</item>
    </string-array>
</resource>