2011-07-27 48 views
12

tengo un documento XML en el que quiero para buscar algunos elementos y si coinciden con algunos criterios quisiera borrarlosBuscar y quitar elementos con elementtree en Python

Sin embargo, me parece que no puede ser capaz de para acceder a la matriz del elemento para que pueda eliminarlo

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 

¿hay alguna manera de hacer esto?

Gracias

Respuesta

13

Puede eliminar elementos secundarios con el método de acuerdo remove. Para eliminar un elemento, debe llamar al método de sus padres remove. Desafortunadamente Element no proporciona una referencia a sus padres, por lo que depende de usted para realizar un seguimiento de los padres/las relaciones del niño (que habla en contra de su uso de elem.findall())

Una solución propuesta podría tener este aspecto:

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

PD: no utilice prop.attrib.get(), utilice prop.get(), como se explica here.

+0

veo. También estoy viendo lxml que, por lo que he leído, proporciona acceso al elemento primario del elemento. Gracias de todos modos – Thomas

+3

Sí, eso es correcto. lxml proporciona una implementación 'ElementTree' con más funciones de las que normalmente establece la interfaz. La clase 'Element' en lxml proporciona el método' getparent() 'para obtener una referencia a un elemento primario. – Constantinius

+2

¿Qué sucede si el elemento secundario tiene más de un elemento desde la raíz? ¿Qué pasa si se trata de profundidades variables? – dwjohnston

2

Usted podría utilizar XPath para seleccionar los padres de un elemento.

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

Excepto si se intenta que no funciona: http://elmpowered.skawaii.net/?p=74

Así que en lugar usted tiene que:

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) 

Es dos búsquedas y una anidada lazo. La búsqueda interna solo se realiza en Elementos conocidos por contener accesorios como primeros niños, pero eso puede no significar mucho en función de su esquema.

1

Usando el hecho de que cada niño debe tener un padre, voy a simplificar el ejemplo de @ kitsu.eb. f usando el comando findall para obtener los hijos y los padres, sus índices serán equivalentes.

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) 
0

Sé que este es un viejo hilo, pero esto seguía apareciendo mientras yo estaba tratando de encontrar una tarea similar. No me gustaba la respuesta aceptada por dos razones:

1) No opera con múltiples niveles anidados de las etiquetas.

2) Se romperá si varias etiquetas XML se eliminan en el mismo nivel de un servicio post-otro. Como cada elemento es un índice de Element._children, no debe eliminar mientras se itera hacia adelante.

creo que una mejor solución más versátil es la siguiente:

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) 

Para el PO, esto debería funcionar - pero no tengo los datos que está trabajando con poner a prueba si es perfecto.

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) 
0

Me gusta usar una expresión XPath para este tipo de filtrado. A menos que sepa lo contrario, dicha expresión se debe aplicar en el nivel raíz, lo que significa que no puedo simplemente obtener un padre y aplicar la misma expresión en ese padre. Sin embargo, me parece que hay una solución agradable y flexible que debería funcionar con cualquier XPath compatible, siempre y cuando ninguno de los nodos buscados sea la raíz. Es algo parecido a esto:

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) 
Cuestiones relacionadas