#######################
Parse XML into a tree
#######################
When your input is XML rather than HTML, use :func:`turbohtml.parse_xml`. It parses under XML 1.0 well-formedness
instead of the WHATWG tree builder, so names stay case-sensitive, ```` self-closes any element, and there is no HTML
recovery -- a malformed document raises. The result is the same navigable :class:`~turbohtml.Document` the HTML path
returns, so you query, edit, and serialize it through the one node API.
******************************
Parse a well-formed document
******************************
Hand :func:`turbohtml.parse_xml` a string of XML. The document's children are its prolog nodes (an optional doctype,
comments, processing instructions) followed by the single root element:
.. testcode::
import turbohtml
doc = turbohtml.parse_xml("One")
catalog = doc.children[0]
book = catalog.children[0]
print(catalog.tag, book.attrs["id"])
print(book.children[0].tag, book.children[0].text)
.. testoutput::
catalog b1
title One
XML elements carry no HTML tag identity, so navigate the tree structurally through :attr:`~turbohtml.Node.children`,
:attr:`~turbohtml.Node.parent`, and the sibling links, and read each :attr:`~turbohtml.Element.tag` verbatim.
*********************************
Read namespaces, CDATA, and PIs
*********************************
turbohtml keeps qualified names verbatim -- an element declared under a prefix keeps its ``prefix:local`` tag, and every
``xmlns``/``xmlns:prefix`` declaration stays as an ordinary attribute. A CDATA section becomes a
:class:`~turbohtml.CData` node and a processing instruction a :class:`~turbohtml.ProcessingInstruction` with a
``target`` and ``data``:
.. testcode::
doc = turbohtml.parse_xml(
''
""
"News"
"raw & co]]>"
""
)
feed = doc.children[0]
print(feed.attrs["xmlns:dc"])
print(feed.children[0].target, feed.children[0].data)
print(feed.children[1].tag)
print(feed.children[2].children[0].data)
.. testoutput::
urn:dc
render mode=fast
dc:title
raw & co
Only the five predefined entities (``&``, ``<``, ``>``, ``"``, ``'``) and numeric character
references such as ``é`` resolve; a document that defines its own entities through a DTD is outside this mode.
*******************************
Catch a well-formedness error
*******************************
Ill-formed XML raises :exc:`~turbohtml.HTMLParseError` at the first violation rather than recovering. Its ``error``
attribute is a :class:`~turbohtml.ParseError` carrying the ``code`` and the source position, so a linter or ingest
pipeline can report exactly what and where:
.. testcode::
try:
turbohtml.parse_xml("")
except turbohtml.HTMLParseError as exception:
print(exception.error.code, exception.error.line, exception.error.col)
.. testoutput::
xml-mismatched-tag 1 8
The same exception surfaces an unclosed tag, an undeclared namespace prefix, an undefined entity, a duplicate attribute,
content outside the root element, or a Namespaces in XML 1.0 violation -- rebinding the reserved ``xml`` or ``xmlns``
prefix, a colon in a processing-instruction target, or two attributes that resolve to the same expanded name.