Creazione di un semplice file XML tramite python


161

Quali sono le mie opzioni se voglio creare un semplice file XML in Python? (per quanto riguarda la biblioteca)

L'xml che voglio assomiglia a:

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

</root>

Risposte:


310

In questi giorni, l'opzione più popolare (e molto semplice) è l' API ElementTree , che è stata inclusa nella libreria standard da Python 2.5.

Le opzioni disponibili per questo sono:

  • ElementTree (implementazione di base, pure-Python di ElementTree. Parte della libreria standard dal 2.5)
  • cElementTree (implementazione C ottimizzata di ElementTree. Disponibile anche nella libreria standard dal 2.5)
  • LXML (basato su libxml2. Offre un ricco superset dell'API ElementTree nonché XPath, selettori CSS e altro)

Ecco un esempio di come generare il tuo documento di esempio usando in-stdlib cElementTree:

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

L'ho provato e funziona, ma presumo che lo spazio bianco non sia significativo. Se hai bisogno del rientro "prettyprint", fammelo sapere e cercherò come farlo. (Potrebbe essere un'opzione specifica per LXML. Non uso molto l'implementazione di stdlib)

Per ulteriori letture, ecco alcuni link utili:

Come nota finale, o cElementTree o LXML dovrebbero essere abbastanza veloci per tutte le tue esigenze (entrambi sono codice C ottimizzato), ma nel caso in cui ti trovi in ​​una situazione in cui devi spremere fino all'ultimo bit di performance, i benchmark su il sito LXML indica che:

  • LXML vince chiaramente per serializzare (generare) XML
  • Come effetto collaterale dell'implementazione dell'attraversamento parent appropriato, LXML è un po 'più lento di cElementTree per l'analisi.

1
@Kasper: non ho un Mac, quindi non posso provare a duplicare il problema. Dimmi la versione di Python e vedrò se riesco a replicarla su Linux.
ssokolow,

4
@nonsensickle Avresti davvero dovuto porre una nuova domanda e poi inviarmi un link in modo che tutti possano beneficiarne. Tuttavia, ti indicherò nella giusta direzione. Le librerie DOM (Document Object Model) creano sempre un modello in memoria in modo da preferire un'implementazione SAX (Simple API for XML). Non ho mai esaminato le implementazioni di SAX ma ecco un tutorial per usare quello in-stdlib per l'output piuttosto che per l'input.
ssokolow,

1
@YonatanSimson Non so come aggiungere quella stringa esatta , poiché ElementTree sembra obbedire solo xml_declaration=Truese specifichi una codifica ... ma, per ottenere un comportamento equivalente, chiama tree.write()così: tree.write("filename.xml", xml_declaration=True, encoding='utf-8')Puoi usare qualsiasi codifica purché tu specifichi esplicitamente uno. ( asciiimporrà che tutti i caratteri Unicode al di fuori del set ASCII a 7 bit siano codificati da entità se non ti fidi che un server Web sia configurato correttamente.)
ssokolow,

1
Solo un promemoria per chiunque altro che cerca di correggere vlaue2a value2: L'errore di battitura è l'output XML richiesto nella domanda iniziale. Fino a quando non cambia, l'errore di battitura qui è effettivamente corretto.
ssokolow,

3
Secondo la documentazione , è cElementTreestato deprezzato in Python 3.3
Stevoisiak il

63

La libreria lxml include una sintassi molto conveniente per la generazione XML, chiamata E-factory . Ecco come farei l'esempio che dai:

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

Produzione:

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

Supporta anche l'aggiunta a un nodo già creato, ad esempio dopo ciò che potresti dire

the_doc.append(FIELD2('another value again', name='hithere'))

3
Se il nome del tag non è conforme alle regole dell'identificatore Python, è possibile utilizzare getattr, ad es getattr(E, "some-tag").
Haridsv,

per me print lxml.etree.tostring stava causando AttributeError: l'oggetto 'lxml.etree._Element' non ha attributo 'etree'. Ha funzionato senza avviare "lxml". come: etree.tostring (the_doc, pretty_print = True)
kodlan

19

Yattag http://www.yattag.org/ o https://github.com/leforestier/yattag fornisce un'API interessante per creare tale documento XML (e anche documenti HTML).

Utilizza il gestore di contesto e la withparola chiave.

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)

così otterrai:

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


4

Per una struttura XML così semplice, potresti non voler coinvolgere un modulo XML completo. Prendi in considerazione un modello di stringa per le strutture più semplici o Jinja per qualcosa di un po 'più complesso. Jinja può gestire il loop su un elenco di dati per produrre l'xml interno dell'elenco dei documenti. Questo è un po 'più complicato con i modelli di stringhe di pitone grezzi

Per un esempio di Jinja, vedi la mia risposta a una domanda simile .

Ecco un esempio di generazione del tuo xml con modelli di stringa.

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

Produzione:

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

Il lato negativo dell'approccio del modello è che non ti lascerai sfuggire <e >gratuitamente. Ho ballato attorno a quel problema estraendo un util daxml.sax


1

Ho appena finito di scrivere un generatore XML, usando il metodo dei modelli di bigh_29 ... è un bel modo di controllare ciò che emetti senza troppi oggetti che si "mettono in mezzo".

Per quanto riguarda il tag e il valore, ho usato due array, uno che indicava il nome e la posizione del tag nell'output xml e un altro che faceva riferimento a un file di parametri con lo stesso elenco di tag. Il file dei parametri, tuttavia, ha anche il numero di posizione nel file di input (csv) corrispondente da cui verranno presi i dati. In questo modo, se ci sono cambiamenti nella posizione dei dati che arrivano dal file di input, il programma non cambia; determina dinamicamente la posizione del campo dati dal tag appropriato nel file dei parametri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.