【问题标题】:Search and remove element with elementTree in Python在 Python 中使用 elementTree 搜索和删除元素
【发布时间】:2011-10-14 09:44:09
【问题描述】:

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

有什么办法可以做到吗?

谢谢

【问题讨论】:

    标签: python elementtree


    【解决方案1】:

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

    【讨论】:

    • 我明白了。我还在查看 lxml,从我读过的内容中可以访问元素的父级。还是谢谢
    • 是的,没错。 lxml 提供了一个ElementTree 实现,其功能比接口正常状态下的要多。 lxml 中的Element 类提供了getparent() 方法来获取对父元素的引用。
    • 如果子元素距根的深度超过一个元素怎么办?如果它处于不同的深度怎么办?
    • "As explain here" - here 链接到 attrib [#](属性)元素属性字典。在可能的情况下,使用 get、set、keys 和 items 来访问元素属性。,但这很难解释。使用get 而不是attrib 的原因是什么?
    【解决方案2】:

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

    这是两个搜索和一个嵌套循环。内部搜索仅针对已知包含作为第一个子项的 props 的元素,但这可能意义不大,具体取决于您的架构。

    【讨论】:

      【解决方案3】:

      我知道这是一个旧线程,但是当我试图找出类似的任务时,它不断弹出。我不喜欢接受的答案有两个原因:

      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)
      

      【讨论】:

        【解决方案4】:

        利用每个孩子都必须有父母这一事实,我将简化@kitsu.eb 的示例。 f 使用 findall 命令获取孩子和父母,他们的索引是等价的。

            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 + '/..')
        
            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':
                            #use the index of the current child to find
                            #its parent and remove the child
                            prop_parents[props.index[prop]].remove(prop)
        

        【讨论】:

          【解决方案5】:

          使用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
          

          【讨论】:

            【解决方案6】:

            我喜欢使用 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)
            

            【讨论】:

              【解决方案7】:

              我也使用 XPath 来解决这个问题,但方式不同:

              root = elem.getroot()    
              elementName = "YourElement"
              #this will find all the parents of the elements with elementName
              for elementParent in root.findall(".//{}/..".format(elementName)):
                 #this will find all the elements under the parent, and remove them
                 for element in elementParent.findall("{}".format(elementName)):
                    elementParent.remove(element)
              

              【讨论】:

                猜你喜欢
                • 2017-01-13
                • 2016-09-17
                • 2018-04-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-03-18
                • 2018-08-27
                • 1970-01-01
                相关资源
                最近更新 更多