Python:如何编辑XML文件的元素?

时间:2019-03-27 19:52:58

标签: python xml

我有一个XML文件,该文件包含许多命名元素,每个元素下面都有几个nodes

<name search = "select ARG: write">
    <version id = "1.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search = "select ARG: bla">
    <version id = "2.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>

我想搜索这个XML文件,并且如果此名称搜索值以select ARG开头(在我的示例中,第一个值为select ARG: write),我想创建一个新的命名元素,但是这次的值为select ARG: write应该为selected ARG: write。这是我尝试过的:

from xml.dom import minidom

xamlfile = r'C:\file.xml'
newxamlfile = r'C:\new.xml'

dom = minidom.parse(xamlfile)

# Fetch the desired elements in the tree.
res = dom.getElementsByTagName('name')

# Loop through all.
for element in res:
    search_name_value = element.getAttribute('search')

    # Filter for the attribute and value.
    if search_name_value.startswith('select ARG:'):
        # In case of match, replace.
        element.setAttribute('search_name', search_name_value.replace('select ARG:', 'selected ARG:'))

# Store the file.
with open(newxamlfile, 'w') as f:
    f.write(dom.toxml())

在这里,我替换了所需的字符串,而不添加新的字符串,而是编辑所需的元素,而不是创建新的元素并将其添加到文件中。

任何建议如何做到这一点?

更新

这是我之前的文件:

<project version="4">
<name search="select ARG: write">
    <version id="1.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search="select ARG: bla">
    <version id="2.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
</project>

这就是我想要我的文件的方式:

<project version="4">
<name search="select ARG: write">
    <version id="1.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search="selected ARG: write">
    <version id="1.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search="select ARG: bla">
    <version id="2.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search="selected ARG: bla">
    <version id="2.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
</project>

编辑

根据@DirtyBit的建议:

xmldoc = minidom.parse(xamlfile)

tags = xmldoc.getElementsByTagName("name")

for item in tags:
    str = item.attributes["search"].value
    if 'select ARG' in str:
        item.attributes["search"].value = item.attributes["search"].value.replace('select ARG', 'selected ARG')

with open(xamlfile, "a+") as f:
    xmldoc.writexml(f)

这很好,但是我有2个问题:

  1. 您可以看到我添加了if语句,因为我想复制并仅在与select ARG的值中创建新节点(并用selected ARG替换),而不重复其他没有达到这个条件。

  2. 在新XML文件的中间,我有这行:

    </element><?xml version="1.0" ?><element>

4 个答案:

答案 0 :(得分:1)

替换属性的一部分的解决方案(在使用新要求更新之前):

import xml.etree.ElementTree as ET

tree = ET.parse('example.xml')

for name in tree.getroot().iterfind('name'):
    if name.attrib['search'].startswith('select ARG'):
        name.attrib['search'] = name.attrib['search'].replace(
            'select ARG', 'selected ARG')

tree.write('example.xml')

使用与上述解决方案相同的replace属性添加新的相同块的解决方案:

import xml.etree.ElementTree as ET

tree = ET.parse('example.xml')

for name in tree.getroot().iterfind('name'):
    if name.attrib['search'].startswith('select ARG'):
        new = ET.Element(name.tag)
        new.attrib['search'] = name.attrib['search'].replace(
            'select ARG', 'selected ARG')
        tree.getroot().append(new)
        for version in name.iterfind('version'):
            new.append(version)

tree.write('example.xml')

来自ElementTree documentation

  

parse(source,parser = None)
将外部XML部分加载到此元素树中。源是文件名或文件对象。解析器是   可选的解析器实例。如果未提供,则标准XMLParser解析器   用来。返回节的根元素。

     

getroot()
返回此树的根元素。

     

iterfind(match)
通过标签名称或路径查找所有匹配的子元素。与getroot()。iterfind(match)相同。返回可迭代的收益   所有匹配的元素都按文档顺序排列。

     

attrib
包含元素属性的字典。请注意,尽管attrib值始终是真正的可变Python字典,   ElementTree实现可以选择使用另一个内部   表示,并仅在有人要求时创建字典。   要利用这些实现,请使用dictionary方法   尽可能在下面。

     

xml.etree.ElementTree.Element(tag,attrib = {},** extra)类
元素类。此类定义Element接口,并提供   该接口的参考实现。

     

元素名称,属性名称和属性值可以是   字节字符串或Unicode字符串。 tag是元素名称。 attrib是一个   可选字典,包含元素属性。额外包含   其他属性,作为关键字参数给出。

     

追加(附加)
  将元素子元素添加到此元素内部子元素列表的末尾

     

写(文件,编码=“ us-ascii”,xml_declaration = None,default_namespace = None,method =“ xml”)
将元素树写入   文件,如XML。 file是文件名,或为以下文件打开的文件对象   写作。 encoding [1]是输出编码(默认为US-ASCII)。   xml_declaration控制是否应将XML声明添加到   文件。 False表示永不,True表示始终,无则表示无   US-ASCII或UTF-8(默认为“无”)。 default_namespace设置   默认的XML名称空间(用于“ xmlns”)。方法是“ xml”,“ html”或   “文本”(默认为“ xml”)。返回编码的字符串。

答案 1 :(得分:1)

下面的代码克隆所需的元素,并将其附加到文档末尾。

55394530.xm l是一个文件,其中包含从示例XML中获取的数据

import xml.etree.ElementTree as ET
import copy
from xml.dom import minidom

tree = ET.parse('55394530.xml')

names_to_duplicate = [e for e in tree.findall('.//name') if e.attrib.get('search').startswith('select ARG:')]
for name in names_to_duplicate:
    clone = copy.deepcopy(name)
    clone.attrib['search'] = clone.attrib['search'].replace('select', 'selected')
    tree.getroot().append(clone)

xmlstr = minidom.parseString(ET.tostring(tree.getroot())).toprettyxml()
with open('out.xml', 'w') as out:
    out.write(xmlstr)

输出

<element>
    <name search="select ARG: 123">
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
    </name>
    <name search="select ARG: 456">
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
    </name>
    <name search="text ARG: 789">
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
    </name>
    <name search="foo ARG: 444">
        <version id="1.1.1">
             <value>bla</value>
            <method>blabla</method>
        </version>
    </name>
    <name search="test ARG: Cancel">
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
    </name>
<name search="selected ARG: 123">
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
    </name>
    <name search="selected ARG: 456">
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
        <version id="1.1.1">
            <value>bla</value>
            <method>blabla</method>
        </version>
    </name>
    </element>

答案 2 :(得分:0)

一旦有了新元素,就使用minidom,以附加模式a+打开文件并写入文件:

list.txt(之前)

<project version="4">
<name search="select ARG: write">
    <version id="1.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search="select ARG: bla">
    <version id="2.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
</project>

因此

from xml.dom import minidom
xmldoc = minidom.parse('list_test.xml')

tags = xmldoc.getElementsByTagName("name")

for item in tags:
    item.attributes["search"].value = item.attributes["search"].value.replace(
        'select ARG', 'selected ARG')

with open("list_test.xml", "a+") as f:
    xmldoc.writexml(f)

list.txt(之后)

<project version="4">
<name search="select ARG: write">
    <version id="1.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search="select ARG: bla">
    <version id="2.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
</project><?xml version="1.0" ?>
<project version="4">
<name search="selected ARG: write">
    <version id="1.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
<name search="selected ARG: bla">
    <version id="2.0.0">
        <value>myVal</value>
        <method>myMethod</method>
    </version>
</name>
</project>

答案 3 :(得分:0)

如果可以使用lxml,这是一个使用XPath谓词检查search属性值的解决方案。

选择正确的name元素后,将进行复制,然后更新属性值。

然后,它将新的name元素添加到原始name元素之后。

XML输入(input.xml)

<project version="4">
    <name search="select ARG: write">
        <version id="1.0.0">
            <value>myVal</value>
            <method>myMethod</method>
        </version>
    </name>
    <name search="select ARG: bla">
        <version id="2.0.0">
            <value>myVal</value>
            <method>myMethod</method>
        </version>
    </name>
</project>

Python

from lxml import etree
from copy import deepcopy

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

for name in tree.xpath("//name[starts-with(@search,'select ARG')]"):
    new_name = deepcopy(name)
    new_name.attrib["search"] = name.get("search").replace("select ARG", "selected ARG")
    name.addnext(new_name)

tree.write("output.xml")

XML输出(具有轻微格式更改的output.xml,以提高可读性)

<project version="4">
    <name search="select ARG: write">
        <version id="1.0.0">
            <value>myVal</value>
            <method>myMethod</method>
        </version>
    </name>
    <name search="selected ARG: write">
        <version id="1.0.0">
            <value>myVal</value>
            <method>myMethod</method>
        </version>
    </name>
    <name search="select ARG: bla">
        <version id="2.0.0">
            <value>myVal</value>
            <method>myMethod</method>
        </version>
    </name>
    <name search="selected ARG: bla">
        <version id="2.0.0">
            <value>myVal</value>
            <method>myMethod</method>
        </version>
    </name>
</project>