2009-12-23 12 views
14

Comment accéder aux attributs NS en utilisant ElementTree?Accéder à l'attribut XMLNS avec Python Elementree?

avec ce qui suit:

<data xmlns="http://www.foo.net/a" xmlns:a="http://www.foo.net/a" book="1" category="ABS" date="2009-12-22"> 

Lorsque je tente de root.get ('xmlns') Je reviens Aucun, Catégorie Date et sont très bien, Toute aide appréciée ..

+3

Je ne peux pas répondre à votre question - mais après avoir lutté contre cette lacune pendant quelques jours, je suis prêt à affirmer que ce n'est pas possible avec l'API ElementTree actuelle. Dans mon application, j'avais besoin de détecter si un attribut xmlns: xlink existait déjà sur l'élément racine, et sinon, ajoutez-le. Il n'est pas possible de tester si un attribut xmlns existe déjà et quoi de plus, ElementTree est heureux de l'ajouter deux fois si vous essayez. Comme zéro ou deux attributs xmlns identiques dans le même élément provoquent une erreur dans la plupart des utilisateurs XML, cela rend très difficile l'utilisation d'ElementTree. –

Répondre

14

Je pense element.tag est ce que vous cherchez. Notez qu'il manque une barre oblique dans votre exemple, donc c'est déséquilibré et n'analyse pas. J'en ai ajouté un dans mon exemple.

>>> from xml.etree import ElementTree as ET 
>>> data = '''<data xmlns="http://www.foo.net/a" 
...     xmlns:a="http://www.foo.net/a" 
...     book="1" category="ABS" date="2009-12-22"/>''' 
>>> element = ET.fromstring(data) 
>>> element 
<Element {http://www.foo.net/a}data at 1013b74d0> 
>>> element.tag 
'{http://www.foo.net/a}data' 
>>> element.attrib 
{'category': 'ABS', 'date': '2009-12-22', 'book': '1'} 

Si vous voulez juste savoir les xmlns URI, vous pouvez le diviser avec une fonction comme:

def tag_uri_and_name(elem): 
    if elem.tag[0] == "{": 
     uri, ignore, tag = elem.tag[1:].partition("}") 
    else: 
     uri = None 
     tag = elem.tag 
    return uri, tag 

Pour beaucoup plus sur et les noms qualifiés namespaces dans ElementTree, voir effbot's examples.

+11

Pourquoi n'y a-t-il pas une fonction comme celle-ci dans la bibliothèque? Il semble que chaque fichier xml avec un espace de noms en aurait besoin. Est-ce que je l'ai manqué? – Clutch

+0

@clutch Je me demande la même chose. Quelqu'un sait une raison pourquoi? – Santa

+0

@rednaw, je ne suis pas convaincu que split soit meilleur. Partition est garanti pour retourner un tuple de exactement trois éléments, split peut renvoyer un nombre arbitraire d'éléments. En pratique, il serait syntaxiquement invalide d'avoir n'importe quoi mais une accolade de fermeture, mais quand même. Je pense que la partition est meilleure. –

7

Regardez la documentation/les exemples des espaces de noms effbot; spécifiquement la fonction parse_map. Il vous montre comment ajouter un attribut * ns_map * à chaque élément qui contient le mappage préfixe/URI qui s'applique à cet élément spécifique.

Toutefois, cela ajoute l'attribut ns_map à tous les éléments. Pour mes besoins, j'ai trouvé que je voulais une carte globale de tous les espaces de noms utilisés pour que l'élément recherche plus facilement et non codé en dur.

Voici ce que je suis venu avec:

import elementtree.ElementTree as ET 

def parse_and_get_ns(file): 
    events = "start", "start-ns" 
    root = None 
    ns = {} 
    for event, elem in ET.iterparse(file, events): 
     if event == "start-ns": 
      if elem[0] in ns and ns[elem[0]] != elem[1]: 
       # NOTE: It is perfectly valid to have the same prefix refer 
       #  to different URI namespaces in different parts of the 
       #  document. This exception serves as a reminder that this 
       #  solution is not robust. Use at your own peril. 
       raise KeyError("Duplicate prefix with different URI found.") 
      ns[elem[0]] = "{%s}" % elem[1] 
     elif event == "start": 
      if root is None: 
       root = elem 
    return ET.ElementTree(root), ns 

Avec cela, vous pouvez analyser un fichier xml et obtenir un dict avec les applications d'espace de noms. Donc, si vous avez un fichier xml comme ce qui suit ("my.xml"):

<?xml version="1.0" encoding="UTF-8" ?> 
<rss version="2.0" 
xmlns:content="http://purl.org/rss/1.0/modules/content/" 
xmlns:dc="http://purl.org/dc/elements/1.1/"\ 
> 
<feed> 
    <item> 
    <title>Foo</title> 
    <dc:creator>Joe McGroin</dc:creator> 
    <description>etc...</description> 
    </item> 
</feed> 
</rss> 

Vous serez en mesure d'utiliser les namepaces xml et obtenir des informations pour des éléments comme dc: creator:

>>> tree, ns = parse_and_get_ns("my.xml") 
>>> ns 
{u'content': '{http://purl.org/rss/1.0/modules/content/}', 
u'dc': '{http://purl.org/dc/elements/1.1/}'} 
>>> item = tree.find("/feed/item") 
>>> item.findtext(ns['dc']+"creator") 
'Joe McGroin' 
+0

Vous avez répondu à mon message sur http://stackoverflow.com/questions/13018024/converting-my-python-script-from-lxml-to-xml-etree/13019393#13019393 –