Convertir Python ElementTree en cadena

84

Siempre que llamo ElementTree.tostring(e), recibo el siguiente mensaje de error:

AttributeError: 'Element' object has no attribute 'getroot'

¿Hay alguna otra forma de convertir un objeto ElementTree en una cadena XML?

Rastrear:

Traceback (most recent call last):
  File "Development/Python/REObjectSort/REObjectResolver.py", line 145, in <module>
    cm = integrateDataWithCsv(cm, csvm)
  File "Development/Python/REObjectSort/REObjectResolver.py", line 137, in integrateDataWithCsv
    xmlstr = ElementTree.tostring(et.getroot(),encoding='utf8',method='xml')
AttributeError: 'Element' object has no attribute 'getroot'
Stevoisiak
fuente

Respuestas:

107

Elementlos objetos no tienen .getroot()método. Desconecta esa llamada y la .tostring()llamada funciona:

xmlstr = ElementTree.tostring(et, encoding='utf8', method='xml')
Martijn Pieters
fuente
7
Para los que llegan tarde al motor de búsqueda: cuando la codificación es 'utf8', antepone el <?xml version='1.0' encoding='utf8'?>encabezado. Cuando es utf-8el encabezado no está incluido. Además, si etera un ElementTree, debes aprobar et.getroot().
Kenji Noguchi
24
En Python 3, encoding='utf8'devuelve una cadena de bytes en lugar de una cadena. Recomiendo usar en su tostring(xml, encoding="unicode")lugar .
Stevoisiak
2
@StevenVascellaro: XML es realmente un formato binario , ya que el formato de datos consiste en bytes en una codificación dada (especificada en la declaración XML en la parte superior, por defecto en UTF-8 si falta). En Python 2, stres el mismo tipo de objeto que bytesPython 3. La salida de bytes es completamente correcta, usar unicodecomo salida es esencialmente una adición que le permite evitar tener que decodificar si necesita una cadena Unicode en su lugar para su caso de uso específico.
Martijn Pieters
1
Según el help, solo encoding="unicode"devolverá una cadena.
cz
45

¿Cómo convierto ElementTree.Elementa una cadena?

Para Python 3:

xml_str = ElementTree.tostring(xml, encoding='unicode')

Para Python 2:

xml_str = ElementTree.tostring(xml, encoding='utf-8')

Para compatibilidad con Python 2 y 3:

xml_str = ElementTree.tostring(xml).decode()

Uso de ejemplo

from xml.etree import ElementTree

xml = ElementTree.Element("Person", Name="John")
xml_str = ElementTree.tostring(xml).decode()
print(xml_str)

Salida:

<Person Name="John" />

Explicación

A pesar de lo que implica el nombre, ElementTree.tostring()devuelve una cadena de bytes por defecto en Python 2 y 3. Este es un problema en Python 3, que usa Unicode para cadenas .

En Python 2, podría usar el strtipo tanto para texto como para datos binarios . Desafortunadamente, esta confluencia de dos conceptos diferentes podría conducir a un código frágil que a veces funciona para cualquier tipo de datos, otras no. [...]

Para que la distinción entre texto y datos binarios sea más clara y pronunciada, [Python 3] hizo que el texto y los datos binarios fueran tipos distintos que no se pueden mezclar a ciegas .

Fuente: Portando el código de Python 2 a Python 3

Si sabemos qué versión de Python se está utilizando, podemos especificar la codificación como unicodeo utf-8. De lo contrario, si necesitamos compatibilidad con Python 2 y 3, podemos usar decode()para convertir al tipo correcto.

Como referencia, he incluido una comparación de .tostring()resultados entre Python 2 y Python 3.

ElementTree.tostring(xml)
# Python 3: b'<Person Name="John" />'
# Python 2: <Person Name="John" />

ElementTree.tostring(xml, encoding='unicode')
# Python 3: <Person Name="John" />
# Python 2: LookupError: unknown encoding: unicode

ElementTree.tostring(xml, encoding='utf-8')
# Python 3: b'<Person Name="John" />'
# Python 2: <Person Name="John" />

ElementTree.tostring(xml).decode()
# Python 3: <Person Name="John" />
# Python 2: <Person Name="John" />

Gracias a Martijn Peters por señalar que el strtipo de datos cambió entre Python 2 y 3.


¿Por qué no usar str ()?

En la mayoría de los escenarios, usar str()sería la forma " canónica " de convertir un objeto en una cadena. Desafortunadamente, usar esto con Elementdevuelve la ubicación del objeto en la memoria como una cadena hexadecimal, en lugar de una representación de cadena de los datos del objeto.

from xml.etree import ElementTree

xml = ElementTree.Element("Person", Name="John")
print(str(xml))  # <Element 'Person' at 0x00497A80>
Stevoisiak
fuente
1
En Python 2 ElementTree.tostring()también genera una cadena de bytes. El strtipo es una cadena de bytes en Python 2 (el strtipo de Python 3 se llama unicodeen Python 2).
Martijn Pieters
1
Esa característica solo se agregó a la versión de Python 3, y no se exportó a Python 2. Si lo fuera, obtendría una unicodecadena de regreso.
Martijn Pieters
0

Extensión de respuesta no latina

Extensión de la respuesta de @ Stevoisiak y manejo de caracteres no latinos. Solo una forma le mostrará los caracteres no latinos. El único método es diferente tanto en Python 3 como en Python 2.

Entrada

xml = ElementTree.fromstring('<Person Name="크리스" />')
xml = ElementTree.Element("Person", Name="크리스")  # Read Note about Python 2

NOTA: En Python 2, al llamar al toString(...)código, asignar xmlcon ElementTree.Element("Person", Name="크리스")generará un error ...

UnicodeDecodeError: 'ascii' codec can't decode byte 0xed in position 0: ordinal not in range(128)

Salida

ElementTree.tostring(xml)
# Python 3 (크리스): b'<Person Name="&#53356;&#47532;&#49828;" />'
# Python 3 (John): b'<Person Name="John" />'

# Python 2 (크리스): <Person Name="&#53356;&#47532;&#49828;" />
# Python 2 (John): <Person Name="John" />


ElementTree.tostring(xml, encoding='unicode')
# Python 3 (크리스): <Person Name="크리스" />             <-------- Python 3
# Python 3 (John): <Person Name="John" />

# Python 2 (크리스): LookupError: unknown encoding: unicode
# Python 2 (John): LookupError: unknown encoding: unicode

ElementTree.tostring(xml, encoding='utf-8')
# Python 3 (크리스): b'<Person Name="\xed\x81\xac\xeb\xa6\xac\xec\x8a\xa4" />'
# Python 3 (John): b'<Person Name="John" />'

# Python 2 (크리스): <Person Name="크리스" />             <-------- Python 2
# Python 2 (John): <Person Name="John" />

ElementTree.tostring(xml).decode()
# Python 3 (크리스): <Person Name="&#53356;&#47532;&#49828;" />
# Python 3 (John): <Person Name="John" />

# Python 2 (크리스): <Person Name="&#53356;&#47532;&#49828;" />
# Python 2 (John): <Person Name="John" />

Christopher Rucinski
fuente