web-dev-qa-db-fra.com

noeud d'accès ElementTree noeud parent

J'utilise le module Python ElementTree intégré. Il est facile d'accéder aux enfants, mais qu'en est-il des nœuds parents ou frères? - cela peut-il être fait efficacement sans traverser tout l’arbre?

48
hoju

Il n’existe aucun support direct sous la forme d’un attribut parent, mais vous pouvez peut-être utiliser les modèles décrits ici / pour obtenir l’effet souhaité. Le one-liner suivant est suggéré (à partir de la publication liée) à créer un mappage enfant-parent pour un arbre entier:

parent_map = dict((c, p) for p in tree.getiterator() for c in p)
36
Vinay Sajip

La réponse de Vinay devrait toujours fonctionner, mais les recommandations suivantes sont recommandées pour Python 2.7+ et 3.2+:

parent_map = {c:p for p in tree.iter() for c in p}

getiterator() est déconseillé en faveur de iter() et il est agréable d'utiliser le nouveau constructeur de compréhension de liste dict.

Deuxièmement, lors de la construction d'un document XML, il est possible qu'un enfant ait plusieurs parents, bien que cela soit supprimé une fois que vous avez sérialisé le document. Si cela compte, essayez ceci:

parent_map = {}
for p in tree.iter():
    for c in p:
        if c in parent_map:
            parent_map[c].append(p)
            # Or raise, if you don't want to allow this.
        else:
            parent_map[c] = [p]
            # Or parent_map[c] = p if you don't want to allow this
15
supergra

Vous pouvez utiliser la notation xpath ... dans ElementTree.

<parent>
     <child id="123">data1</child>
</parent>

xml.findall('.//child[@id="123"]...')
>> [<Element 'parent'>]
7
josven

Comme mentionné dans Obtenir l'élément parent après avoir utilisé la méthode find (xml.etree.ElementTree) vous devrez effectuer une recherche indirecte pour parent .

<a>
 <b>
  <c>data</c>
  <d>data</d>    
 </b>
</a>

En supposant que vous avez créé un élément etree dans la variable xml, vous pouvez utiliser:

 In[1] parent = xml.find('.//c/..')
 In[2] child = parent.find('./c')

Résultant en:

Out[1]: <Element 'b' at 0x00XXXXXX> 
Out[2]: <Element 'c' at 0x00XXXXXX>

Le parent supérieur se trouverait ainsi: secondparent=xml.find('.//c/../..') étant <Element 'a' at 0x00XXXXXX>

4
Vaasha

Une autre façon si vous voulez juste le parent d'un seul sous-élément et que vous connaissez le xpath du sous-élément.

parentElement = subElement.find(xpath+"/..")
2
MK at Soho

Si vous utilisez lxml, j'ai pu obtenir l'élément parent avec les éléments suivants:

parent_node = next(child_node.iterancestors())

Cela déclenchera une exception StopIteration si l'élément n'a pas d'ancêtres - préparez-vous à le détecter si vous rencontrez ce scénario.

1
Shadow

Coller ici ma réponse de https://stackoverflow.com/a/54943960/492336 :

J'ai eu un problème similaire et j'ai eu un peu de créativité. Il s'avère que rien ne nous empêche d'ajouter les informations de filiation nous-mêmes. Nous pourrons plus tard le déshabiller une fois que nous n'en aurons plus besoin.

def addParentInfo(et):
    for child in et:
        child.attrib['__my_parent__'] = et
        addParentInfo(child)

def stripParentInfo(et):
    for child in et:
        child.attrib.pop('__my_parent__', 'None')
        stripParentInfo(child)

def getParent(et):
    if '__my_parent__' in et.attrib:
        return et.attrib['__my_parent__']
    else:
        return None

# Example usage

tree = ...
addParentInfo(tree.getroot())
el = tree.findall(...)[0]
parent = getParent(el)
while parent:
    doSomethingWith(parent)
    parent = getParent(parent)
stripParentInfo(tree.getroot())
0
sashoalm

Le sélecteur XPath '..' ne peut pas être utilisé pour récupérer le noeud parent sous 3.5.3 ni 3.6.1 (au moins sous OSX),

import xml.etree.ElementTree as ET
root = ET.fromstring('<parent><child></child></parent>')
child = root.find('child')
parent = child.find('..') # retrieve the parent
parent is None # unexpected answer True

La dernière réponse brise tous les espoirs ...

0
jlaurens