在Python中使用elementTree搜索和删除元素

时间:2011-07-27 15:45:06

标签: python elementtree

我有一个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

我有办法做到这一点吗?

由于

5 个答案:

答案 0 :(得分:20)

您可以使用相应的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)

PS:不要使用prop.attrib.get(),请使用prop.get(),如here所述。

答案 1 :(得分:2)

您可以使用xpath选择Element的父级。

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)

这是两个搜索和一个嵌套循环。内部搜索仅在已知包含道具作为第一个子项的元素上,但根据您的模式,这可能并不重要。

答案 2 :(得分:0)

我知道这是一个老线程,但在我试图找出类似的任务时,这种情况一直在突然出现。由于两个原因,我不喜欢接受的答案:

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)

对于OP,这应该有用 - 但我没有你正在使用的数据来测试它是否完美。

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)

答案 3 :(得分:0)

我喜欢使用XPath表达式进行此类过滤。除非我不知道,否则必须在根级别应用这样的表达式,这意味着我不能只获取父级并在该父级上应用相同的表达式。但是,在我看来,只要没有所搜索的节点是根,就可以使用任何受支持的XPath,这是一个很好且灵活的解决方案。它是这样的:

root = elem.getroot()
# Find all nodes matching the filter string (flt)
nodes = root.findall(flt)
while len(nodes):
    # As long as there are nodes, there should be parents
    # Get the first of all parents to the found nodes
    parent = root.findall(flt+'/..')[0]
    # Use this parent to remove the first node
    parent.remove(nodes[0])
    # Find all remaining nodes
    nodes = root.findall(flt)

答案 4 :(得分:0)

使用lxml模块的解决方案

from lxml import etree

root = ET.fromstring(xml_str)
for e in root.findall('.//{http://some.name.space}node'):
parent = e.getparent()
for child in parent.find('./{http://some.name.space}node'):
    try:
        parent.remove(child)
    except ValueError:
        pass