尝试使用 python 在 XML 中插入一个元素,但我的代码没有作为单独的元素添加,而是作为子元素添加

Trying to Insert an element in XML using python, but instead of getting added as separate Element my code is adding as SubElement

我有如下示例 xml 文件,我想在 <item>Production</item>

之后添加一个元素 <item>DEV</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”的节点的父节点。您还需要具有文本“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")

Can you please share some example on how to use lxml for adding a new element. I tried with solution @Pierre-Loic shared and it works fine but after adding new Element it does not create new line.

下面是在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>

Can we add indentation to the Elements. The requirement is to have <item>...</item> with 4 indentations. Right now i see with only 2 for all elements. The original file has 4 but this script after adding new Element, it is changing the indentation to 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>