Konwertuj Python ElementTree na ciąg

86

Za każdym razem, gdy dzwonię ElementTree.tostring(e), otrzymuję następujący komunikat o błędzie:

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

Czy istnieje inny sposób przekonwertowania obiektu ElementTree na ciąg XML?

TraceBack:

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
źródło

Odpowiedzi:

112

Elementobiekty nie mają .getroot()metody. Porzuć to połączenie, a .tostring()połączenie zadziała:

xmlstr = ElementTree.tostring(et, encoding='utf8', method='xml')

Musisz użyć tylko .getroot()wtedy, gdy masz ElementTreeinstancję .

Inne notatki:

  • W ten sposób powstaje bajtest , który w Pythonie 3 jest bytestypem.
    Jeśli musisz mieć strobiekt, masz dwie możliwości:

    1. Dekoduj wynikową wartość bajtów z UTF-8: xmlstr.decode("utf8")

    2. Użyj encoding='unicode'; pozwala to uniknąć cyklu kodowania / dekodowania:

      xmlstr = ElementTree.tostring(et, encoding='unicode', method='xml')
      
  • Jeśli chcesz mieć wartość zakodowaną przez testowanie bajtów UTF-8 lub korzystasz z Pythona 2, weź pod uwagę, że ElementTree nie wykrywa poprawnie utf8standardowego kodowania XML, więc doda <?xml version='1.0' encoding='utf8'?>deklarację. Użyj utf-8lub UTF-8(z myślnikiem), jeśli chcesz temu zapobiec. W przypadku korzystania encoding="unicode"bez deklaracji nagłówek jest dodawany.

Martijn Pieters
źródło
47

Jak przekonwertować ElementTree.Elementna ciąg?

W przypadku Pythona 3:

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

W przypadku Pythona 2:

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

Poniższy kod jest zgodny zarówno z językiem Python 2, jak i 3, ale działa tylko dla znaków łacińskich :

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

Przykładowe użycie

from xml.etree import ElementTree

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

Wynik:

<Person Name="John" />

Wyjaśnienie

Pomimo tego, co sugeruje nazwa, ElementTree.tostring()domyślnie zwraca bajt testowy w Pythonie 2 i 3. Jest to problem w Pythonie 3, który używa Unicode dla łańcuchów .

W Pythonie 2 możesz użyć strtypu zarówno dla danych tekstowych, jak i binarnych . Niestety, to zbieżność dwóch różnych koncepcji może prowadzić do kruchego kodu, który czasami działał dla obu rodzajów danych, a czasami nie. […]

Aby rozróżnienie między tekstem a danymi binarnymi było wyraźniejsze i wyraźniejsze, [Python 3] uczynił tekst i dane binarne odrębnymi typami, których nie można ślepo mieszać ze sobą .

Źródło: przenoszenie kodu Python 2 na Python 3

Jeśli wiemy, która wersja Pythona jest używana, możemy określić kodowanie jako unicodelub utf-8. W przeciwnym razie, jeśli potrzebujemy kompatybilności z Pythonem 2 i 3, możemy użyć decode()do konwersji na właściwy typ.

Dla porównania zamieściłem porównanie .tostring()wyników między Pythonem 2 i Pythonem 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" />

Podziękowania dla Martijn Peters za wskazanie, że strtyp danych zmienił się między Pythonem 2 i 3.


Dlaczego nie użyć str ()?

W większości scenariuszy użycie str()byłoby „ kanonicznym ” sposobem konwersji obiektu na łańcuch. Niestety, użycie tego z Elementzwraca lokalizację obiektu w pamięci jako ciąg szesnastkowy, a nie ciąg znaków reprezentujący dane obiektu.

from xml.etree import ElementTree

xml = ElementTree.Element("Person", Name="John")
print(str(xml))  # <Element 'Person' at 0x00497A80>
Stevoisiak
źródło
1
W Pythonie 2 ElementTree.tostring()również generuje bytestring. strTypu jest bytestring Pythona 2 (Python 3'S strtypu nazywa unicodePythona 2).
Martijn Pieters
1
Ta funkcja została dodana tylko do wersji Pythona 3 i nie została przeniesiona unicodewstecz do Pythona 2. Gdyby tak było, otrzymywałbyś z powrotem ciąg znaków.
Martijn Pieters
1

Rozszerzenie odpowiedzi innej niż łacińska

Rozszerzenie do odpowiedzi @ Stevoisiak i radzenie sobie ze znakami spoza alfabetu łacińskiego. Tylko jeden sposób spowoduje wyświetlenie znaków spoza alfabetu łacińskiego. Jedna metoda jest inna w Pythonie 3 i Pythonie 2.

Wejście

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

UWAGA: W Pythonie 2 podczas wywoływania toString(...)kodu przypisanie za xmlpomocą ElementTree.Element("Person", Name="크리스")spowoduje zgłoszenie błędu ...

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

Wynik

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

Christophera Rucińskiego
źródło
1
Dobre wezwanie do znaków spoza alfabetu łacińskiego. Zaktualizowałem swój post, aby o tym wspomnieć.
Stevoisiak
0

Jeśli potrzebujesz tego tylko do debugowania, aby zobaczyć, jak wygląda XML, zamiast tego print(xml.etree.ElementTree.tostring(e))możesz użyć w dumpten sposób:

xml.etree.ElementTree.dump(e)

Działa to zarówno z obiektami jak Elementi ElementTreejako e, więc nie powinno być takiej potrzeby getroot.

Dokumentacjadump mówi:

xml.etree.ElementTree.dump(elem)

Zapisuje drzewo elementów lub strukturę elementów do sys.stdout. Ta funkcja powinna być używana tylko do debugowania.

Dokładny format wyjściowy zależy od implementacji. W tej wersji jest zapisywany jako zwykły plik XML.

elem jest drzewem elementów lub pojedynczym elementem.

Zmieniono w wersji 3.8 : dump()Funkcja zachowuje teraz kolejność atrybutów określoną przez użytkownika.

Paul Tobias
źródło