So schreiben Sie eine XML-Deklaration mit xml.etree.ElementTree

72

Ich generiere ein XML-Dokument in Python mit einem ElementTree, aber die tostringFunktion enthält keine XML-Deklaration bei der Konvertierung in Klartext.

from xml.etree.ElementTree import Element, tostring

document = Element('outer')
node = SubElement(document, 'inner')
node.NewValue = 1
print tostring(document)  # Outputs "<outer><inner /></outer>"

Ich benötige meine Zeichenfolge, um die folgende XML-Deklaration einzuschließen:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>

Es scheint jedoch keinen dokumentierten Weg zu geben, dies zu tun.

Gibt es eine geeignete Methode zum Rendern der XML-Deklaration in einem ElementTree?

Roman Alexander
quelle

Antworten:

115

Ich bin überrascht festzustellen, dass es keinen Weg zu geben scheint ElementTree.tostring(). Sie können ElementTree.ElementTree.write()Ihr XML-Dokument jedoch verwenden , um es in eine gefälschte Datei zu schreiben:

from io import BytesIO
from xml.etree import ElementTree as ET

document = ET.Element('outer')
node = ET.SubElement(document, 'inner')
et = ET.ElementTree(document)

f = BytesIO()
et.write(f, encoding='utf-8', xml_declaration=True) 
print(f.getvalue())  # your XML file, encoded as UTF-8

Siehe diese Frage . Selbst dann glaube ich nicht, dass Sie Ihr "eigenständiges" Attribut erhalten können, ohne es selbst zu schreiben.

wrgrs
quelle
Warum definieren Sie hier die Variable "Knoten"?
Rail Suleymanov
6
Dank dieser Zeile hat et.write (f, encoding = 'utf-8', xml_declaration = True) meinen Tag gerettet
Vineel
Gibt es einen hübschen Druckparameter für ´et.write () ´? oder eine andere Möglichkeit, eine XML mit Zeilenumbrüchen zu generieren?
Jan-Seins
@ Jan-Seins Ja, siehe stackoverflow.com/questions/749796/…
Dai
29

Ich würde lxml verwenden (siehe http://lxml.de/api.html ).

Dann kannst du:

from lxml import etree
document = etree.Element('outer')
node = etree.SubElement(document, 'inner')
print(etree.tostring(document, xml_declaration=True))
Glormph
quelle
21

Wenn Sie das einschließen encoding='utf8', erhalten Sie einen XML-Header :

xml.etree.ElementTree.tostring schreibt eine XML-Codierungsdeklaration mit encoding = 'utf8'

Beispiel für Python-Code (funktioniert mit Python 2 und 3):

import xml.etree.ElementTree as ElementTree

tree = ElementTree.ElementTree(
    ElementTree.fromstring('<xml><test>123</test></xml>')
)
root = tree.getroot()

print('without:')
print(ElementTree.tostring(root, method='xml'))
print('')
print('with:')
print(ElementTree.tostring(root, encoding='utf8', method='xml'))

Python 2-Ausgabe:

$ python2 example.py
without:
<xml><test>123</test></xml>

with:
<?xml version='1.0' encoding='utf8'?>
<xml><test>123</test></xml>

Bei Python 3 werden Sie das bPräfix beachten, das angibt, dass Byte-Literale zurückgegeben werden (genau wie bei Python 2):

$ python3 example.py
without:
b'<xml><test>123</test></xml>'

with:
b"<?xml version='1.0' encoding='utf8'?>\n<xml><test>123</test></xml>"
Alexander O'Mara
quelle
In Python 3 werden beim Drucken Escapezeichen in der Deklaration angezeigt. <?xml version=\'1.0\' encoding=\'utf8\'?>
Stevoisiak
Was bei dieser Antwort geholfen hat, ist die Frage, warum Sie so viel davon getan haben, Elementree.Elementree(Elementree.fromstring(...und ich erkenne jetzt, dass fromstringein elementnicht ein zurückgegeben wird ElementTree, während die parseMethode ein zurückgibt ElementTree. Dies macht den Versuch, eine XML-Datei in einer Testsuite mithilfe einer Zeichenfolge zu verspotten, sehr verwirrend! Wenn Sie dieses Element nehmen und ausführen tostring, werden diese Codierungs- und Methodenparameter zugelassen, aber in der Ausgabe fehlt die <?xmlDeklarationszeile, und jetzt sehe ich, dass dies nicht das vollständige Dokument ist.
Davos
Beachten Sie, dass dies utf8KEINE gültige Zeichencodierungszeichenfolge ist. Aus diesem Grund fügt Python3 die Deklaration hinzu und gibt das Ganze als Bytes anstelle von Zeichenfolgen zurück.
Geburt
@mbirth, daher sollte die Methode als "tobytes" und nicht als "tostring" angegeben werden.
Marek Marczak
@MarekMarczak Nein, das XML sollte gelesen werden encoding='utf-8', um gültig zu sein.
Geburt
3

Ich bin kürzlich auf dieses Problem gestoßen, nachdem ich den Code ein wenig ausgegraben habe. Ich habe festgestellt, dass das folgende Codeausschnitt eine Definition der Funktion ist ElementTree.write

def write(self, file, encoding="us-ascii"):
    assert self._root is not None
    if not hasattr(file, "write"):
        file = open(file, "wb")
    if not encoding:
        encoding = "us-ascii"
    elif encoding != "utf-8" and encoding != "us-ascii":
        file.write("<?xml version='1.0' encoding='%s'?>\n" % 
     encoding)
    self._write(file, self._root, encoding, {})

Die Antwort lautet also: Wenn Sie den XML-Header in Ihre Datei schreiben müssen, setzen Sie ein encodinganderes Argument als utf-8oder us-ascii, zUTF-8

Alijandro
quelle
Es wäre ein netter, wenn auch spröder Hack, aber es scheint nicht zu funktionieren (die Codierung ist wahrscheinlich vorher in Kleinbuchstaben geschrieben). Es ElementTree.ElementTree.write()ist auch dokumentiert, dass es einen xml_declarationParameter gibt (siehe die akzeptierte Antwort). Hat ElementTree.tostring()aber nicht diesen Parameter, der die Methode war, die in der ursprünglichen Frage gestellt wurde.
Quentin Pradet
2

Das minimale Arbeitsbeispiel mit ElementTreePaketnutzung:

import xml.etree.ElementTree as ET

document = ET.Element('outer')
node = ET.SubElement(document, 'inner')
node.text = '1'
res = ET.tostring(document, encoding='utf8', method='xml').decode()
print(res)

Die Ausgabe ist:

<?xml version='1.0' encoding='utf8'?>
<outer><inner>1</inner></outer>
Andriy
quelle
3
Leider ist utf8 'kein gültiges XML, aber' UTF-8 'ist docs.python.org/3.8/library/xml.etree.elementtree.html#id6
Luftangriff
1

Eine andere ziemlich einfache Option besteht darin, den gewünschten Header wie folgt mit der XML-Zeichenfolge zu verketten:

xml = (bytes('<?xml version="1.0" encoding="UTF-8"?>\n', encoding='utf-8') + ET.tostring(root))
xml = xml.decode('utf-8')
with open('invoice.xml', 'w+') as f:
    f.write(xml)
Novak
quelle
Es gibt diesen Fehler: TypeError: str () nimmt höchstens 1 Argument (2 gegeben)
Panduranga Rao Sadhu
1

Einfach

Beispiel für Python 2 und 3 ( Codierungsparameter muss utf8 sein ):

import xml.etree.ElementTree as ElementTree

tree = ElementTree.ElementTree(ElementTree.fromstring('<xml><test>123</test></xml>'))
root = tree.getroot()
print(ElementTree.tostring(root, encoding='utf8', method='xml'))

Ab Python 3.8 gibt es den Parameter xml_declaration für dieses Zeug:

Neu in Version 3.8: Die Parameter xml_declaration und default_namespace.

xml.etree.ElementTree.tostring (Element, encoding = "us-ascii", method = "xml", *, xml_declaration = Keine, default_namespace = Keine, short_empty_elements = True) Erzeugt eine Zeichenfolgendarstellung eines XML-Elements, einschließlich aller Unterelemente . Element ist eine Elementinstanz. Codierung 1 ist die Ausgabecodierung (Standard ist US-ASCII). Verwenden Sie encoding = "unicode", um eine Unicode-Zeichenfolge zu generieren (andernfalls wird ein Bytestring generiert). Methode ist entweder "xml", "html" oder "text" (Standard ist "xml"). xml_declaration, default_namespace und short_empty_elements haben dieselbe Bedeutung wie in ElementTree.write (). Gibt eine (optional) codierte Zeichenfolge zurück, die die XML-Daten enthält.

Beispiel für Python 3.8 und höher:

import xml.etree.ElementTree as ElementTree

tree = ElementTree.ElementTree(ElementTree.fromstring('<xml><test>123</test></xml>'))
root = tree.getroot()
print(ElementTree.tostring(root, encoding='unicode', method='xml', xml_declaration=True))
Kirill Malakhov
quelle
1

xml_declaration Argument

Gibt es eine geeignete Methode zum Rendern der XML-Deklaration in einem ElementTree?

JA, und es besteht keine Notwendigkeit, die .tostringFunktion zu verwenden. Gemäß der ElementTree-Dokumentation sollten Sie ein ElementTree-Objekt erstellen, Element und Unterelemente erstellen, die Wurzel des Baums festlegen und schließlich das xml_declarationArgument in verwenden.write function verwenden, damit die Deklarationszeile in der Ausgabedatei enthalten ist.

Sie können es so machen:

import xml.etree.ElementTree as ET

tree = ET.ElementTree("tree")

document = ET.Element("outer")
node1 = ET.SubElement(document, "inner")
node1.text = "text"

tree._setroot(document)
tree.write("./output.xml", encoding = "UTF-8", xml_declaration = True)  

Und die Ausgabedatei lautet:

<?xml version='1.0' encoding='UTF-8'?>
<outer><inner>text</inner></outer>
Mohsen Rahchamani
quelle
0

Ich würde ET verwenden :

try:
    from lxml import etree
    print("running with lxml.etree")
except ImportError:
    try:
        # Python 2.5
        import xml.etree.cElementTree as etree
        print("running with cElementTree on Python 2.5+")
    except ImportError:
        try:
            # Python 2.5
            import xml.etree.ElementTree as etree
            print("running with ElementTree on Python 2.5+")
        except ImportError:
            try:
                # normal cElementTree install
                import cElementTree as etree
                print("running with cElementTree")
            except ImportError:
               try:
                   # normal ElementTree install
                   import elementtree.ElementTree as etree
                   print("running with ElementTree")
               except ImportError:
                   print("Failed to import ElementTree from any known place")

document = etree.Element('outer')
node = etree.SubElement(document, 'inner')
print(etree.tostring(document, encoding='UTF-8', xml_declaration=True))
Alessandro
quelle
0

Dies funktioniert, wenn Sie nur drucken möchten. Beim Versuch, es an eine Datei zu senden, wird ein Fehler angezeigt ...

import xml.dom.minidom as minidom
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element, SubElement, Comment, tostring

def prettify(elem):
    rough_string = ET.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="  ")
Rebecca Fallon
quelle
0

Aufnahme von "Standalone" in die Erklärung

Ich habe keine Alternative zum Hinzufügen des standaloneArguments in der Dokumentation gefunden und die ET.tostingFunktion so angepasst , dass sie als Argument verwendet wird.

from xml.etree import ElementTree as ET

# Sample
document = ET.Element('outer')
node = ET.SubElement(document, 'inner')
et = ET.ElementTree(document)

 # Function that you need   
 def tostring(element, declaration, encoding=None, method=None,):
     class dummy:
         pass
     data = []
     data.append(declaration+"\n")
     file = dummy()
     file.write = data.append
     ET.ElementTree(element).write(file, encoding, method=method)
     return "".join(data)
# Working example
xdec = """<?xml version="1.0" encoding="UTF-8" standalone="no" ?>"""    
xml = tostring(document, encoding='utf-8', declaration=xdec)
GM
quelle