2010-05-19 7 views
4

j'ai un document XML suivant:filtrage xml avec python

<node0> 
    <node1> 
     <node2 a1="x1"> ... </node2> 
     <node2 a1="x2"> ... </node2> 
     <node2 a1="x1"> ... </node2> 
    </node1> 
</node0> 

Je veux filtrer node2 quand a1="x2". L'utilisateur fournit les valeurs xpath et attribut qui doivent être testées et filtrées. J'ai regardé quelques solutions en python comme BeautifulSoup mais elles sont trop compliquées et ne préservent pas le cas du texte. Je veux garder le document comme avant avec des choses filtrées.

Pouvez-vous recommander une solution simple et succincte? Cela ne devrait pas être trop compliqué de l'apparence de celui-ci. Le document xml réel n'est pas aussi simple que ci-dessus mais l'idée est la même.

+1

Votre document XML est pas bien formé. Il manque des caractères "/". – BoltBait

Répondre

6

Il utilise xml.etree.ElementTree qui est dans la bibliothèque standard:

import xml.etree.ElementTree as xee 
data='''\ 
<node1> 
    <node2 a1="x1"> ... </node2> 
    <node2 a1="x2"> ... </node2> 
    <node2 a1="x1"> ... </node2> 
</node1> 
''' 
doc=xee.fromstring(data) 

for tag in doc.findall('node2'): 
    if tag.attrib['a1']=='x2': 
     doc.remove(tag) 
print(xee.tostring(doc)) 
# <node1> 
# <node2 a1="x1"> ... </node2> 
# <node2 a1="x1"> ... </node2> 
# </node1> 

Il utilise lxml, qui ne sont pas dans la bibliothèque standard, mais a a more powerful syntax:

import lxml.etree 
data='''\ 
<node1> 
    <node2 a1="x1"> ... </node2> 
    <node2 a1="x2"> ... </node2> 
    <node2 a1="x1"> ... </node2> 
</node1> 
''' 
doc = lxml.etree.XML(data) 
e=doc.find('node2/[@a1="x2"]') 
doc.remove(e) 
print(lxml.etree.tostring(doc)) 

# <node1> 
# <node2 a1="x1"> ... </node2> 
# <node2 a1="x1"> ... </node2> 
# </node1> 

Edit: Si node2 est enterré plus profondément dans le XML, alors vous pouvez parcourir tous les tags, vérifier chaque tag parent pour voir si l'élément node2 est l'un de ses enfants Et le supprimer si si:

En utilisant seulement xml.etree.ElementTree:

doc=xee.fromstring(data) 
for parent in doc.getiterator(): 
    for child in parent.findall('node2'): 
     if child.attrib['a1']=='x2': 
      parent.remove(child) 

En utilisant lxml:

doc = lxml.etree.XML(data) 
for parent in doc.iter('*'): 
    child=parent.find('node2/[@a1="x2"]') 
    if child is not None: 
     parent.remove(child) 
+0

Je me suis rendu compte que cela ne fonctionne pas lorsque node2 est encore plus imbriqué à l'intérieur. Je peux trouver la balise en utilisant doc.findAll ('node1/node2'), mais je ne peux pas l'enlever de doc. Aucune suggestion? – user236215

+0

@saminny, j'ai ajouté un code montrant comment faire face à cette situation. – unutbu

+0

Cela fonctionne parfaitement. Vous êtes un génie :) – user236215