在Python中使用ElementTree搜索并删除元素

35 投票
9 回答
81088 浏览
提问于 2025-04-16 22:22

我有一个XML文档,我想在里面找一些元素,如果它们符合某些条件,我想把它们删除。

但是,我好像无法访问到这些元素的父级,这样就不能把它们删除。

file = open('test.xml', "r")
elem = ElementTree.parse(file)

namespace = "{http://somens}"

props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
    type = prop.attrib.get('type', None)
    if type == 'json':
        value = json.loads(prop.attrib['value'])
        if value['name'] == 'Page1.Button1':
            #here I need to access the parent of prop
            # in order to delete the prop

有没有办法可以做到这一点呢?

谢谢!

9 个回答

6

你可以使用xpath来选择一个元素的父级。

file = open('test.xml', "r")
elem = ElementTree.parse(file)

namespace = "{http://somens}"

props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
    type = prop.get('type', None)
    if type == 'json':
        value = json.loads(prop.attrib['value'])
        if value['name'] == 'Page1.Button1':
            # Get parent and remove this prop
            parent = prop.find("..")
            parent.remove(prop)

http://docs.python.org/2/library/xml.etree.elementtree.html#supported-xpath-syntax

不过如果你尝试这样做,可能会发现它并不奏效:http://elmpowered.skawaii.net/?p=74

所以你需要这样做:

file = open('test.xml', "r")
elem = ElementTree.parse(file)

namespace = "{http://somens}"
search = './/{0}prop'.format(namespace)

# Use xpath to get all parents of props    
prop_parents = elem.findall(search + '/..')
for parent in prop_parents:
    # Still have to find and iterate through child props
    for prop in parent.findall(search):
        type = prop.get('type', None)
        if type == 'json':
            value = json.loads(prop.attrib['value'])
            if value['name'] == 'Page1.Button1':
                parent.remove(prop)

这需要进行两次搜索和一个嵌套循环。内部的搜索只针对那些已知包含属性作为第一个子元素的元素,但这可能根据你的数据结构而有所不同。

9

我知道这个话题已经很老了,但在我尝试解决类似问题时,这个帖子一直出现在我的眼前。我对接受的答案有两个不满意的地方:

1) 它不能处理多个嵌套层级的标签。

2) 如果在同一层级中连续删除多个xml标签,它会出错。因为每个元素都是Element._children的一个索引,所以在向前遍历时不应该删除。

我认为一个更好、更灵活的解决方案是这个:

import xml.etree.ElementTree as et
file = 'test.xml'
tree = et.parse(file)
root = tree.getroot()

def iterator(parents, nested=False):
    for child in reversed(parents):
        if nested:
            if len(child) >= 1:
                iterator(child)
        if True:  # Add your entire condition here
            parents.remove(child)

iterator(root, nested=True)

对于提问者来说,这个应该能工作——但我没有你正在使用的数据,所以无法测试它是否完美。

import xml.etree.ElementTree as et
file = 'test.xml'
tree = et.parse(file)

namespace = "{http://somens}"
props = tree.findall('.//{0}prop'.format(namespace))

def iterator(parents, nested=False):
    for child in reversed(parents):
        if nested:
            if len(child) >= 1:
                iterator(child)
        if prop.attrib.get('type') == 'json':
            value = json.loads(prop.attrib['value'])
            if value['name'] == 'Page1.Button1':
                parents.remove(child)

iterator(props, nested=True)
43

你可以使用相应的 remove 方法来删除子元素。要删除一个元素,你需要调用它父元素的 remove 方法。不过,Element 并没有提供指向其父元素的引用,所以你需要自己跟踪父子关系(这就不太适合使用 elem.findall() 了)。

一个建议的解决方案可能是这样的:

root = elem.getroot()
for child in root:
    if child.name != "prop":
        continue
    if True:# TODO: do your check here!
        root.remove(child)

另外,别用 prop.attrib.get(),要用 prop.get(),具体解释可以参考 这里

撰写回答