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?

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.


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


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

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

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


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>

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)

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

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>

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

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

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.