Créer un fichier XML simple à l'aide de python

161

Quelles sont mes options si je souhaite créer un simple fichier XML en python? (au niveau de la bibliothèque)

Le xml que je veux ressemble à:

<root>
 <doc>
     <field1 name="blah">some value1</field1>
     <field2 name="asdfasd">some vlaue2</field2>
 </doc>

</root>
Blankman
la source

Réponses:

310

De nos jours, l'option la plus populaire (et la plus simple) est l' API ElementTree , qui est incluse dans la bibliothèque standard depuis Python 2.5.

Les options disponibles pour cela sont:

  • ElementTree (Implémentation basique, pure-Python d'ElementTree. Partie de la bibliothèque standard depuis 2.5)
  • cElementTree (implémentation optimisée en C d'ElementTree. Aussi offert dans la bibliothèque standard depuis 2.5)
  • LXML (basé sur libxml2. Offre un sur-ensemble riche de l'API ElementTree ainsi que XPath, les sélecteurs CSS, etc.)

Voici un exemple de la façon de générer votre exemple de document à l'aide du cElementTree in-stdlib:

import xml.etree.cElementTree as ET

root = ET.Element("root")
doc = ET.SubElement(root, "doc")

ET.SubElement(doc, "field1", name="blah").text = "some value1"
ET.SubElement(doc, "field2", name="asdfasd").text = "some vlaue2"

tree = ET.ElementTree(root)
tree.write("filename.xml")

Je l'ai testé et cela fonctionne, mais je suppose que les espaces blancs ne sont pas significatifs. Si vous avez besoin d'une indentation "prettyprint", faites-le moi savoir et je chercherai comment faire cela. (Cela peut être une option spécifique à LXML. Je n'utilise pas beaucoup l'implémentation stdlib)

Pour plus d'informations, voici quelques liens utiles:

En guise de note finale, cElementTree ou LXML devrait être suffisamment rapide pour tous vos besoins (les deux sont du code C optimisé), mais si vous vous trouvez dans une situation où vous avez besoin de tirer le maximum de performances, les repères sur le site LXML indique que:

  • LXML gagne clairement pour la sérialisation (génération) de XML
  • En tant qu'effet secondaire de l'implémentation d'une traversée parent correcte, LXML est un peu plus lent que cElementTree pour l'analyse.
ssokolow
la source
1
@Kasper: Je n'ai pas de Mac donc je ne peux pas essayer de dupliquer le problème. Dites-moi la version Python et je verrai si je peux la répliquer sous Linux.
ssokolow
4
@nonsensickle Vous auriez vraiment dû poser une nouvelle question et m'envoyer ensuite un lien vers celle-ci pour que tout le monde puisse en profiter. Cependant, je vais vous orienter dans la bonne direction. Les bibliothèques DOM (Document Object Model) construisent toujours un modèle en mémoire, vous voulez donc une implémentation SAX (Simple API for XML) à la place. Je n'ai jamais examiné les implémentations SAX mais voici un tutoriel pour utiliser celui in-stdlib pour la sortie plutôt que l'entrée.
ssokolow
1
@YonatanSimson Je ne sais pas comment ajouter cette chaîne exacte , car ElementTree semble n'obéir que xml_declaration=Truesi vous spécifiez un encodage ... mais, pour obtenir un comportement équivalent, appelez tree.write()comme ceci: tree.write("filename.xml", xml_declaration=True, encoding='utf-8')Vous pouvez utiliser n'importe quel encodage tant que vous spécifiez explicitement une. ( asciiforcera tous les caractères Unicode en dehors de l'ensemble ASCII 7 bits à être encodés par entité si vous ne faites pas confiance à un serveur Web pour être configuré correctement.)
ssokolow
1
Il suffit de rappeler à quelqu'un d' autre qui essaie de corriger vlaue2à value2: La faute de frappe est la sortie XML demandé dans la question initiale. Jusqu'à ce que cela change, la faute de frappe ici est en fait correcte.
ssokolow
3
Selon la documentation , a cElementTreeété déprécié en Python 3.3
Stevoisiak
63

La bibliothèque lxml comprend une syntaxe très pratique pour la génération XML, appelée E-factory . Voici comment je donnerais l'exemple que vous donnez:

#!/usr/bin/python
import lxml.etree
import lxml.builder    

E = lxml.builder.ElementMaker()
ROOT = E.root
DOC = E.doc
FIELD1 = E.field1
FIELD2 = E.field2

the_doc = ROOT(
        DOC(
            FIELD1('some value1', name='blah'),
            FIELD2('some value2', name='asdfasd'),
            )   
        )   

print lxml.etree.tostring(the_doc, pretty_print=True)

Production:

<root>
  <doc>
    <field1 name="blah">some value1</field1>
    <field2 name="asdfasd">some value2</field2>
  </doc>
</root>

Il prend également en charge l'ajout à un nœud déjà fait, par exemple après ce qui précède, vous pourriez dire

the_doc.append(FIELD2('another value again', name='hithere'))
rescdsk
la source
3
Si le nom de l'étiquette ne se conforme pas aux règles d'identification Python, vous pouvez ensuite utiliser getattr, par exemple, getattr(E, "some-tag").
haridsv
pour moi, print lxml.etree.tostring provoquait une erreur AttributeError: l'objet «lxml.etree._Element» n'a pas d'attribut «etree». Cela fonctionnait sans démarrer «lxml». comme: etree.tostring (the_doc, pretty_print = True)
kodlan
19

Yattag http://www.yattag.org/ ou https://github.com/leforestier/yattag fournit une API intéressante pour créer un tel document XML (et aussi des documents HTML).

Il utilise le gestionnaire de contexte et le withmot - clé.

from yattag import Doc, indent

doc, tag, text = Doc().tagtext()

with tag('root'):
    with tag('doc'):
        with tag('field1', name='blah'):
            text('some value1')
        with tag('field2', name='asdfasd'):
            text('some value2')

result = indent(
    doc.getvalue(),
    indentation = ' '*4,
    newline = '\r\n'
)

print(result)

vous obtiendrez donc:

<root>
    <doc>
        <field1 name="blah">some value1</field1>
        <field2 name="asdfasd">some value2</field2>
    </doc>
</root>
scls
la source
4

Pour une structure XML aussi simple, vous ne voudrez peut-être pas impliquer un module XML complet. Considérez un modèle de chaîne pour les structures les plus simples, ou Jinja pour quelque chose d'un peu plus complexe. Jinja peut gérer la boucle sur une liste de données pour produire le xml interne de votre liste de documents. C'est un peu plus délicat avec les modèles de chaîne python bruts

Pour un exemple de Jinja, voir ma réponse à une question similaire .

Voici un exemple de génération de votre xml avec des modèles de chaînes.

import string
from xml.sax.saxutils import escape

inner_template = string.Template('    <field${id} name="${name}">${value}</field${id}>')

outer_template = string.Template("""<root>
 <doc>
${document_list}
 </doc>
</root>
 """)

data = [
    (1, 'foo', 'The value for the foo document'),
    (2, 'bar', 'The <value> for the <bar> document'),
]

inner_contents = [inner_template.substitute(id=id, name=name, value=escape(value)) for (id, name, value) in data]
result = outer_template.substitute(document_list='\n'.join(inner_contents))
print result

Production:

<root>
 <doc>
    <field1 name="foo">The value for the foo document</field1>
    <field2 name="bar">The &lt;value&gt; for the &lt;bar&gt; document</field2>
 </doc>
</root>

Le point négatif de l'approche du modèle est que vous ne serez pas échapper de <et >gratuitement. J'ai dansé autour de ce problème en tirant un utilitaire dexml.sax

bigh_29
la source
1

Je viens de finir d'écrire un générateur xml, en utilisant la méthode des modèles de bigh_29 ... c'est une bonne façon de contrôler ce que vous produisez sans que trop d'objets soient «gênants».

En ce qui concerne la balise et la valeur, j'ai utilisé deux tableaux, l'un qui donnait le nom et la position de la balise dans le xml de sortie et un autre qui faisait référence à un fichier de paramètres ayant la même liste de balises. Le fichier de paramètres, cependant, a également le numéro de position dans le fichier d'entrée (csv) correspondant d'où les données seront extraites. De cette façon, s'il y a des changements dans la position des données provenant du fichier d'entrée, le programme ne change pas; il calcule dynamiquement la position du champ de données à partir de la balise appropriée dans le fichier de paramètres.

Cloughie
la source