Erstellen einer einfachen XML-Datei mit Python

160

Was sind meine Optionen, wenn ich eine einfache XML-Datei in Python erstellen möchte? (bibliotheksmäßig)

Die XML, die ich möchte, sieht aus wie:

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

</root>
Blankman
quelle

Antworten:

309

Heutzutage ist die beliebteste (und sehr einfache) Option die ElementTree-API , die seit Python 2.5 in der Standardbibliothek enthalten ist.

Die verfügbaren Optionen dafür sind:

  • ElementTree (Grundlegende, reine Python-Implementierung von ElementTree. Teil der Standardbibliothek seit 2.5)
  • cElementTree (Optimierte C-Implementierung von ElementTree. Wird seit 2.5 auch in der Standardbibliothek angeboten.)
  • LXML (Basierend auf libxml2. Bietet eine umfangreiche Obermenge der ElementTree-API sowie XPath, CSS-Selektoren und mehr.)

Hier ist ein Beispiel, wie Sie Ihr Beispieldokument mit dem in-stdlib cElementTree generieren:

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

Ich habe es getestet und es funktioniert, aber ich gehe davon aus, dass Leerzeichen nicht signifikant sind. Wenn Sie einen "Prettyprint" -Einzug benötigen, lassen Sie es mich wissen und ich werde nachschlagen, wie das geht. (Möglicherweise handelt es sich um eine LXML-spezifische Option. Ich verwende die stdlib-Implementierung nicht häufig.)

Zur weiteren Lektüre hier einige nützliche Links:

Abschließend sei angemerkt, dass entweder cElementTree oder LXML für alle Ihre Anforderungen schnell genug sein sollten (beide sind optimierter C-Code) Die LXML-Site gibt Folgendes an:

  • LXML gewinnt eindeutig für die Serialisierung (Generierung) von XML
  • Als Nebeneffekt bei der Implementierung einer ordnungsgemäßen übergeordneten Durchquerung ist LXML beim Parsen etwas langsamer als cElementTree.
ssokolow
quelle
1
@ Kasper: Ich habe keinen Mac, daher kann ich nicht versuchen, das Problem zu duplizieren. Sagen Sie mir die Python-Version und ich werde sehen, ob ich sie unter Linux replizieren kann.
ssokolow
4
@nonsensickle Du hättest wirklich eine neue Frage stellen und mir dann einen Link dazu schicken sollen, damit jeder davon profitieren kann. Ich werde Sie jedoch in die richtige Richtung weisen. DOM-Bibliotheken (Document Object Model) erstellen immer ein In-Memory-Modell, sodass Sie stattdessen eine SAX-Implementierung (Simple API for XML) wünschen. Ich habe mich noch nie mit SAX-Implementierungen befasst, aber hier ist ein Tutorial für die Verwendung der in-stdlib für die Ausgabe anstelle der Eingabe.
Ssokolow
1
@YonatanSimson Ich weiß nicht, wie ich genau diese Zeichenfolge hinzufügen soll , da ElementTree nur zu gehorchen scheint, xml_declaration=Truewenn Sie eine Codierung angeben. Um ein gleichwertiges Verhalten zu erzielen , rufen Sie Folgendes auf tree.write(): tree.write("filename.xml", xml_declaration=True, encoding='utf-8')Sie können jede Codierung verwenden, solange Sie dies explizit angeben einer. ( asciiErzwingt, dass alle Unicode-Zeichen außerhalb des 7-Bit-ASCII-Satzes
entitätscodiert werden
1
Nur zur Erinnerung an alle anderen , die versucht zu korrigieren , vlaue2zu value2: Der Tippfehler in der gewünschten XML - Ausgabe in der ursprünglichen Frage. Bis sich das ändert, die Tippfehler hier tatsächlich ist richtig.
ssokolow
3
Gemäß der Dokumentation , cElementTreewurde in Python 3.3 abgeschrieben
Stevoisiak
63

Die lxml-Bibliothek enthält eine sehr praktische Syntax für die XML-Generierung, die E-Factory . So würde ich das Beispiel machen, das Sie geben:

#!/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)

Ausgabe:

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

Es unterstützt auch das Hinzufügen zu einem bereits erstellten Knoten, z. B. nach dem oben Gesagten

the_doc.append(FIELD2('another value again', name='hithere'))
residdsk
quelle
3
Wenn der Name des Tags nicht den Python-Bezeichnerregeln entspricht, können Sie getattrz getattr(E, "some-tag").
Haridsv
Für mich verursachte print lxml.etree.tostring AttributeError: Das Objekt 'lxml.etree._Element' hat kein Attribut 'etree'. Es funktionierte ohne "lxml" zu starten. wie: etree.tostring (the_doc, pretty_print = True)
kodlan
19

Yattag http://www.yattag.org/ oder https://github.com/leforestier/yattag bietet eine interessante API zum Erstellen eines solchen XML-Dokuments (und auch von HTML-Dokumenten).

Es verwendet Kontextmanager und withSchlüsselwort.

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)

So erhalten Sie:

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

Für eine so einfache XML-Struktur möchten Sie möglicherweise kein vollständiges XML-Modul einbeziehen. Betrachten Sie eine Zeichenfolgenvorlage für die einfachsten Strukturen oder Jinja für etwas Komplexeres. Jinja kann eine Liste von Daten durchlaufen, um die innere XML Ihrer Dokumentliste zu erstellen. Bei rohen Python-String-Vorlagen ist das etwas schwieriger

Ein Jinja-Beispiel finden Sie in meiner Antwort auf eine ähnliche Frage .

Hier ist ein Beispiel für das Generieren Ihrer XML mit Zeichenfolgenvorlagen.

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

Ausgabe:

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

Der Wehrmutstropfen der Vorlage Ansatz ist , dass Sie nicht entkommen erhalten von <und >kostenlos. Ich habe um dieses Problem herum getanzt, indem ich einen Util aus gezogen habexml.sax

bigh_29
quelle
1

Ich habe gerade einen XML-Generator mit der Template-Methode von bigh_29 fertig geschrieben. Dies ist eine gute Möglichkeit, die Ausgabe zu steuern, ohne dass zu viele Objekte im Weg stehen.

Für das Tag und den Wert habe ich zwei Arrays verwendet, eines, das den Tag-Namen und die Position in der Ausgabe-XML angibt, und eines, das auf eine Parameterdatei mit derselben Liste von Tags verweist. Die Parameterdatei hat jedoch auch die Positionsnummer in der entsprechenden Eingabedatei (CSV), aus der die Daten entnommen werden. Auf diese Weise ändert sich das Programm nicht, wenn sich die Position der aus der Eingabedatei eingehenden Daten ändert. Es berechnet dynamisch die Datenfeldposition aus dem entsprechenden Tag in der Parameterdatei.

Cloughie
quelle