api.txt 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. ===========================
  2. APIs specific to lxml.etree
  3. ===========================
  4. lxml.etree tries to follow established APIs wherever possible. Sometimes,
  5. however, the need to expose a feature in an easy way led to the invention of a
  6. new API. This page describes the major differences and a few additions to the
  7. main ElementTree API.
  8. For a complete reference of the API, see the `generated API
  9. documentation`_.
  10. Separate pages describe the support for `parsing XML`_, executing `XPath and
  11. XSLT`_, `validating XML`_ and interfacing with other XML tools through the
  12. `SAX-API`_.
  13. lxml is extremely extensible through `XPath functions in Python`_, custom
  14. `Python element classes`_, custom `URL resolvers`_ and even `at the C-level`_.
  15. .. _`parsing XML`: parsing.html
  16. .. _`XPath and XSLT`: xpathxslt.html
  17. .. _`validating XML`: validation.html
  18. .. _`SAX-API`: sax.html
  19. .. _`XPath functions in Python`: extensions.html
  20. .. _`Python element classes`: element_classes.html
  21. .. _`at the C-level`: capi.html
  22. .. _`URL resolvers`: resolvers.html
  23. .. _`generated API documentation`: api/index.html
  24. .. contents::
  25. ..
  26. 1 lxml.etree
  27. 2 Other Element APIs
  28. 3 Trees and Documents
  29. 4 Iteration
  30. 5 Error handling on exceptions
  31. 6 Error logging
  32. 7 Serialisation
  33. 8 Incremental XML generation
  34. 9 CDATA
  35. 10 XInclude and ElementInclude
  36. 11 write_c14n on ElementTree
  37. ..
  38. >>> from io import BytesIO
  39. >>> def StringIO(s=None):
  40. ... if isinstance(s, str): s = s.encode("UTF-8")
  41. ... return BytesIO(s)
  42. >>> from collections import deque
  43. >>> try: unicode = unicode
  44. ... except NameError: unicode = str
  45. lxml.etree
  46. ----------
  47. lxml.etree tries to follow the `ElementTree API`_ wherever it can. There are
  48. however some incompatibilities (see `compatibility`_). The extensions are
  49. documented here.
  50. .. _`ElementTree API`: http://effbot.org/zone/element-index.htm
  51. .. _`compatibility`: compatibility.html
  52. If you need to know which version of lxml is installed, you can access the
  53. ``lxml.etree.LXML_VERSION`` attribute to retrieve a version tuple. Note,
  54. however, that it did not exist before version 1.0, so you will get an
  55. AttributeError in older versions. The versions of libxml2 and libxslt are
  56. available through the attributes ``LIBXML_VERSION`` and ``LIBXSLT_VERSION``.
  57. The following examples usually assume this to be executed first:
  58. .. sourcecode:: pycon
  59. >>> from lxml import etree
  60. ..
  61. >>> import sys
  62. >>> from lxml import etree as _etree
  63. >>> if sys.version_info[0] >= 3:
  64. ... class etree_mock(object):
  65. ... def __getattr__(self, name): return getattr(_etree, name)
  66. ... def tostring(self, *args, **kwargs):
  67. ... s = _etree.tostring(*args, **kwargs)
  68. ... if isinstance(s, bytes) and bytes([10]) in s: s = s.decode("utf-8") # CR
  69. ... if s[-1] == '\n': s = s[:-1]
  70. ... return s
  71. ... else:
  72. ... class etree_mock(object):
  73. ... def __getattr__(self, name): return getattr(_etree, name)
  74. ... def tostring(self, *args, **kwargs):
  75. ... s = _etree.tostring(*args, **kwargs)
  76. ... if s[-1] == '\n': s = s[:-1]
  77. ... return s
  78. >>> etree = etree_mock()
  79. Other Element APIs
  80. ------------------
  81. While lxml.etree itself uses the ElementTree API, it is possible to replace
  82. the Element implementation by `custom element subclasses`_. This has been
  83. used to implement well-known XML APIs on top of lxml. For example, lxml ships
  84. with a data-binding implementation called `objectify`_, which is similar to
  85. the `Amara bindery`_ tool.
  86. lxml.etree comes with a number of `different lookup schemes`_ to customize the
  87. mapping between libxml2 nodes and the Element classes used by lxml.etree.
  88. .. _`custom element subclasses`: element_classes.html
  89. .. _`objectify`: objectify.html
  90. .. _`different lookup schemes`: element_classes.html#setting-up-a-class-lookup-scheme
  91. .. _`Amara bindery`: http://uche.ogbuji.net/tech/4suite/amara/
  92. Trees and Documents
  93. -------------------
  94. Compared to the original ElementTree API, lxml.etree has an extended tree
  95. model. It knows about parents and siblings of elements:
  96. .. sourcecode:: pycon
  97. >>> root = etree.Element("root")
  98. >>> a = etree.SubElement(root, "a")
  99. >>> b = etree.SubElement(root, "b")
  100. >>> c = etree.SubElement(root, "c")
  101. >>> d = etree.SubElement(root, "d")
  102. >>> e = etree.SubElement(d, "e")
  103. >>> b.getparent() == root
  104. True
  105. >>> print(b.getnext().tag)
  106. c
  107. >>> print(c.getprevious().tag)
  108. b
  109. Elements always live within a document context in lxml. This implies that
  110. there is also a notion of an absolute document root. You can retrieve an
  111. ElementTree for the root node of a document from any of its elements.
  112. .. sourcecode:: pycon
  113. >>> tree = d.getroottree()
  114. >>> print(tree.getroot().tag)
  115. root
  116. Note that this is different from wrapping an Element in an ElementTree. You
  117. can use ElementTrees to create XML trees with an explicit root node:
  118. .. sourcecode:: pycon
  119. >>> tree = etree.ElementTree(d)
  120. >>> print(tree.getroot().tag)
  121. d
  122. >>> etree.tostring(tree)
  123. b'<d><e/></d>'
  124. ElementTree objects are serialised as complete documents, including
  125. preceding or trailing processing instructions and comments.
  126. All operations that you run on such an ElementTree (like XPath, XSLT, etc.)
  127. will understand the explicitly chosen root as root node of a document. They
  128. will not see any elements outside the ElementTree. However, ElementTrees do
  129. not modify their Elements:
  130. .. sourcecode:: pycon
  131. >>> element = tree.getroot()
  132. >>> print(element.tag)
  133. d
  134. >>> print(element.getparent().tag)
  135. root
  136. >>> print(element.getroottree().getroot().tag)
  137. root
  138. The rule is that all operations that are applied to Elements use either the
  139. Element itself as reference point, or the absolute root of the document that
  140. contains this Element (e.g. for absolute XPath expressions). All operations
  141. on an ElementTree use its explicit root node as reference.
  142. Iteration
  143. ---------
  144. The ElementTree API makes Elements iterable to supports iteration over their
  145. children. Using the tree defined above, we get:
  146. .. sourcecode:: pycon
  147. >>> [ child.tag for child in root ]
  148. ['a', 'b', 'c', 'd']
  149. To iterate in the opposite direction, use the builtin ``reversed()`` function
  150. that exists in Python 2.4 and later.
  151. Tree traversal should use the ``element.iter()`` method:
  152. .. sourcecode:: pycon
  153. >>> [ el.tag for el in root.iter() ]
  154. ['root', 'a', 'b', 'c', 'd', 'e']
  155. lxml.etree also supports this, but additionally features an extended API for
  156. iteration over the children, following/preceding siblings, ancestors and
  157. descendants of an element, as defined by the respective XPath axis:
  158. .. sourcecode:: pycon
  159. >>> [ child.tag for child in root.iterchildren() ]
  160. ['a', 'b', 'c', 'd']
  161. >>> [ child.tag for child in root.iterchildren(reversed=True) ]
  162. ['d', 'c', 'b', 'a']
  163. >>> [ sibling.tag for sibling in b.itersiblings() ]
  164. ['c', 'd']
  165. >>> [ sibling.tag for sibling in c.itersiblings(preceding=True) ]
  166. ['b', 'a']
  167. >>> [ ancestor.tag for ancestor in e.iterancestors() ]
  168. ['d', 'root']
  169. >>> [ el.tag for el in root.iterdescendants() ]
  170. ['a', 'b', 'c', 'd', 'e']
  171. Note how ``element.iterdescendants()`` does not include the element
  172. itself, as opposed to ``element.iter()``. The latter effectively
  173. implements the 'descendant-or-self' axis in XPath.
  174. All of these iterators support one (or more, since lxml 3.0) additional
  175. arguments that filter the generated elements by tag name:
  176. .. sourcecode:: pycon
  177. >>> [ child.tag for child in root.iterchildren('a') ]
  178. ['a']
  179. >>> [ child.tag for child in d.iterchildren('a') ]
  180. []
  181. >>> [ el.tag for el in root.iterdescendants('d') ]
  182. ['d']
  183. >>> [ el.tag for el in root.iter('d') ]
  184. ['d']
  185. >>> [ el.tag for el in root.iter('d', 'a') ]
  186. ['a', 'd']
  187. Note that the order of the elements is determined by the iteration order,
  188. which is the document order in most cases (except for preceding siblings
  189. and ancestors, where it is the reversed document order). The order of
  190. the tag selection arguments is irrelevant, as you can see in the last
  191. example.
  192. The most common way to traverse an XML tree is depth-first, which
  193. traverses the tree in document order. This is implemented by the
  194. ``.iter()`` method. While there is no dedicated method for
  195. breadth-first traversal, it is almost as simple if you use the
  196. ``collections.deque`` type that is available in Python 2.4 and later.
  197. .. sourcecode:: pycon
  198. >>> root = etree.XML('<root><a><b/><c/></a><d><e/></d></root>')
  199. >>> print(etree.tostring(root, pretty_print=True, encoding='unicode'))
  200. <root>
  201. <a>
  202. <b/>
  203. <c/>
  204. </a>
  205. <d>
  206. <e/>
  207. </d>
  208. </root>
  209. >>> queue = deque([root])
  210. >>> while queue:
  211. ... el = queue.popleft() # pop next element
  212. ... queue.extend(el) # append its children
  213. ... print(el.tag)
  214. root
  215. a
  216. d
  217. b
  218. c
  219. e
  220. See also the section on the utility functions ``iterparse()`` and
  221. ``iterwalk()`` in the `parser documentation`_.
  222. .. _`parser documentation`: parsing.html#iterparse-and-iterwalk
  223. Error handling on exceptions
  224. ----------------------------
  225. Libxml2 provides error messages for failures, be it during parsing, XPath
  226. evaluation or schema validation. The preferred way of accessing them is
  227. through the local ``error_log`` property of the respective evaluator or
  228. transformer object. See their documentation for details.
  229. However, lxml also keeps a global error log of all errors that occurred at the
  230. application level. Whenever an exception is raised, you can retrieve the
  231. errors that occured and "might have" lead to the problem from the error log
  232. copy attached to the exception:
  233. .. sourcecode:: pycon
  234. >>> etree.clear_error_log()
  235. >>> broken_xml = '''
  236. ... <root>
  237. ... <a>
  238. ... </root>
  239. ... '''
  240. >>> try:
  241. ... etree.parse(StringIO(broken_xml))
  242. ... except etree.XMLSyntaxError, e:
  243. ... pass # just put the exception into e
  244. ..
  245. >>> etree.clear_error_log()
  246. >>> try:
  247. ... etree.parse(StringIO(broken_xml))
  248. ... except etree.XMLSyntaxError:
  249. ... import sys; e = sys.exc_info()[1]
  250. Once you have caught this exception, you can access its ``error_log`` property
  251. to retrieve the log entries or filter them by a specific type, error domain or
  252. error level:
  253. .. sourcecode:: pycon
  254. >>> log = e.error_log.filter_from_level(etree.ErrorLevels.FATAL)
  255. >>> print(log)
  256. <string>:4:8:FATAL:PARSER:ERR_TAG_NAME_MISMATCH: Opening and ending tag mismatch: a line 3 and root
  257. <string>:5:1:FATAL:PARSER:ERR_TAG_NOT_FINISHED: Premature end of data in tag root line 2
  258. This might look a little cryptic at first, but it is the information that
  259. libxml2 gives you. At least the message at the end should give you a hint
  260. what went wrong and you can see that the fatal errors (FATAL) happened during
  261. parsing (PARSER) lines 4, column 8 and line 5, column 1 of a string (<string>,
  262. or the filename if available). Here, PARSER is the so-called error domain,
  263. see ``lxml.etree.ErrorDomains`` for that. You can get it from a log entry
  264. like this:
  265. .. sourcecode:: pycon
  266. >>> entry = log[0]
  267. >>> print(entry.domain_name)
  268. PARSER
  269. >>> print(entry.type_name)
  270. ERR_TAG_NAME_MISMATCH
  271. >>> print(entry.filename)
  272. <string>
  273. There is also a convenience attribute ``last_error`` that returns the last
  274. error or fatal error that occurred:
  275. .. sourcecode:: pycon
  276. >>> entry = e.error_log.last_error
  277. >>> print(entry.domain_name)
  278. PARSER
  279. >>> print(entry.type_name)
  280. ERR_TAG_NOT_FINISHED
  281. >>> print(entry.filename)
  282. <string>
  283. Error logging
  284. -------------
  285. lxml.etree supports logging libxml2 messages to the Python stdlib logging
  286. module. This is done through the ``etree.PyErrorLog`` class. It disables the
  287. error reporting from exceptions and forwards log messages to a Python logger.
  288. To use it, see the descriptions of the function ``etree.useGlobalPythonLog``
  289. and the class ``etree.PyErrorLog`` for help. Note that this does not affect
  290. the local error logs of XSLT, XMLSchema, etc.
  291. Serialisation
  292. -------------
  293. lxml.etree has direct support for pretty printing XML output. Functions like
  294. ``ElementTree.write()`` and ``tostring()`` support it through a keyword
  295. argument:
  296. .. sourcecode:: pycon
  297. >>> root = etree.XML("<root><test/></root>")
  298. >>> etree.tostring(root)
  299. b'<root><test/></root>'
  300. >>> print(etree.tostring(root, pretty_print=True))
  301. <root>
  302. <test/>
  303. </root>
  304. Note the newline that is appended at the end when pretty printing the
  305. output. It was added in lxml 2.0.
  306. By default, lxml (just as ElementTree) outputs the XML declaration only if it
  307. is required by the standard:
  308. .. sourcecode:: pycon
  309. >>> unicode_root = etree.Element( u"t\u3120st" )
  310. >>> unicode_root.text = u"t\u0A0Ast"
  311. >>> etree.tostring(unicode_root, encoding="utf-8")
  312. b'<t\xe3\x84\xa0st>t\xe0\xa8\x8ast</t\xe3\x84\xa0st>'
  313. >>> print(etree.tostring(unicode_root, encoding="iso-8859-1"))
  314. <?xml version='1.0' encoding='iso-8859-1'?>
  315. <t&#12576;st>t&#2570;st</t&#12576;st>
  316. Also see the general remarks on `Unicode support`_.
  317. .. _`Unicode support`: parsing.html#python-unicode-strings
  318. You can enable or disable the declaration explicitly by passing another
  319. keyword argument for the serialisation:
  320. .. sourcecode:: pycon
  321. >>> print(etree.tostring(root, xml_declaration=True))
  322. <?xml version='1.0' encoding='ASCII'?>
  323. <root><test/></root>
  324. >>> unicode_root.clear()
  325. >>> etree.tostring(unicode_root, encoding="UTF-16LE",
  326. ... xml_declaration=False)
  327. b'<\x00t\x00 1s\x00t\x00/\x00>\x00'
  328. Note that a standard compliant XML parser will not consider the last line
  329. well-formed XML if the encoding is not explicitly provided somehow, e.g. in an
  330. underlying transport protocol:
  331. .. sourcecode:: pycon
  332. >>> notxml = etree.tostring(unicode_root, encoding="UTF-16LE",
  333. ... xml_declaration=False)
  334. >>> root = etree.XML(notxml) #doctest: +ELLIPSIS
  335. Traceback (most recent call last):
  336. ...
  337. lxml.etree.XMLSyntaxError: ...
  338. Since version 2.3, the serialisation can override the internal subset
  339. of the document with a user provided DOCTYPE:
  340. .. sourcecode:: pycon
  341. >>> xml = '<!DOCTYPE root>\n<root/>'
  342. >>> tree = etree.parse(StringIO(xml))
  343. >>> print(etree.tostring(tree))
  344. <!DOCTYPE root>
  345. <root/>
  346. >>> print(etree.tostring(tree,
  347. ... doctype='<!DOCTYPE root SYSTEM "/tmp/test.dtd">'))
  348. <!DOCTYPE root SYSTEM "/tmp/test.dtd">
  349. <root/>
  350. The content will be encoded, but otherwise copied verbatimly into the
  351. output stream. It is therefore left to the user to take care for a
  352. correct doctype format, including the name of the root node.
  353. Incremental XML generation
  354. --------------------------
  355. Since version 3.1, lxml provides an ``xmlfile`` API for incrementally
  356. generating XML using the ``with`` statement. It's main purpose is to
  357. freely and safely mix surrounding elements with pre-built in-memory
  358. trees, e.g. to write out large documents that consist mostly of
  359. repetitive subtrees (like database dumps). But it can be useful in
  360. many cases where memory consumption matters or where XML is naturally
  361. generated in sequential steps. Since lxml 3.4.1, there is an equivalent
  362. context manager for HTML serialisation called ``htmlfile``.
  363. The API can serialise to real files (given as file path or file
  364. object), as well as file-like objects, e.g. ``io.BytesIO()``.
  365. Here is a simple example::
  366. >>> f = BytesIO()
  367. >>> with etree.xmlfile(f) as xf:
  368. ... with xf.element('abc'):
  369. ... xf.write('text')
  370. >>> print(f.getvalue().decode('utf-8'))
  371. <abc>text</abc>
  372. ``xmlfile()`` accepts a file path as first argument, or a file(-like)
  373. object, as in the example above. In the first case, it takes care to
  374. open and close the file itself, whereas file(-like) objects are not
  375. closed by default. This is left to the code that opened them. Since
  376. lxml 3.4, however, you can pass the argument ``close=True`` to make
  377. lxml call the object's ``.close()`` method when exiting the xmlfile
  378. context manager.
  379. To insert pre-constructed Elements and subtrees, just pass them
  380. into ``write()``::
  381. >>> f = BytesIO()
  382. >>> with etree.xmlfile(f) as xf:
  383. ... with xf.element('abc'):
  384. ... with xf.element('in'):
  385. ...
  386. ... for value in '123':
  387. ... # construct a really complex XML tree
  388. ... el = etree.Element('xyz', attr=value)
  389. ...
  390. ... xf.write(el)
  391. ...
  392. ... # no longer needed, discard it right away!
  393. ... el = None
  394. >>> print(f.getvalue().decode('utf-8'))
  395. <abc><in><xyz attr="1"/><xyz attr="2"/><xyz attr="3"/></in></abc>
  396. It is a common pattern to have one or more nested ``element()``
  397. blocks, and then build in-memory XML subtrees in a loop (using the
  398. ElementTree API, the builder API, XSLT, or whatever) and write them
  399. out into the XML file one after the other. That way, they can be
  400. removed from memory right after their construction, which can largely
  401. reduce the memory footprint of an application, while keeping the
  402. overall XML generation easy, safe and correct.
  403. Together with Python coroutines, this can be used to generate XML
  404. in an asynchronous, non-blocking fashion, e.g. for a stream protocol
  405. like the instant messaging protocol
  406. `XMPP <https://en.wikipedia.org/wiki/Extensible_Messaging_and_Presence_Protocol>`_::
  407. def writer(out_stream):
  408. with xmlfile(out_stream) as xf:
  409. with xf.element('{http://etherx.jabber.org/streams}stream'):
  410. try:
  411. while True:
  412. el = (yield)
  413. xf.write(el)
  414. xf.flush()
  415. except GeneratorExit:
  416. pass
  417. w = writer(stream)
  418. next(w) # start writing (run up to 'yield')
  419. Then, whenever XML elements are available for writing, call
  420. ::
  421. w.send(element)
  422. And when done::
  423. w.close()
  424. Note the additional ``xf.flush()`` call in the example above, which is
  425. available since lxml 3.4. Normally, the output stream is buffered to
  426. avoid excessive I/O calls. Whenever the internal buffer fills up, its
  427. content is written out. In the case above, however, we want to make
  428. sure that each message that we write (i.e. each element subtree) is
  429. written out immediately, so we flush the content explicitly at the
  430. right point.
  431. Alternatively, if buffering is not desired at all, it can be disabled
  432. by passing the flag ``buffered=False`` into ``xmlfile()`` (also since
  433. lxml 3.4).
  434. CDATA
  435. -----
  436. By default, lxml's parser will strip CDATA sections from the tree and
  437. replace them by their plain text content. As real applications for
  438. CDATA are rare, this is the best way to deal with this issue.
  439. However, in some cases, keeping CDATA sections or creating them in a
  440. document is required to adhere to existing XML language definitions.
  441. For these special cases, you can instruct the parser to leave CDATA
  442. sections in the document:
  443. .. sourcecode:: pycon
  444. >>> parser = etree.XMLParser(strip_cdata=False)
  445. >>> root = etree.XML('<root><![CDATA[test]]></root>', parser)
  446. >>> root.text
  447. 'test'
  448. >>> etree.tostring(root)
  449. b'<root><![CDATA[test]]></root>'
  450. Note how the ``.text`` property does not give any indication that the
  451. text content is wrapped by a CDATA section. If you want to make sure
  452. your data is wrapped by a CDATA block, you can use the ``CDATA()``
  453. text wrapper:
  454. .. sourcecode:: pycon
  455. >>> root.text = 'test'
  456. >>> root.text
  457. 'test'
  458. >>> etree.tostring(root)
  459. b'<root>test</root>'
  460. >>> root.text = etree.CDATA(root.text)
  461. >>> root.text
  462. 'test'
  463. >>> etree.tostring(root)
  464. b'<root><![CDATA[test]]></root>'
  465. XInclude and ElementInclude
  466. ---------------------------
  467. You can let lxml process xinclude statements in a document by calling the
  468. xinclude() method on a tree:
  469. .. sourcecode:: pycon
  470. >>> data = StringIO('''\
  471. ... <doc xmlns:xi="http://www.w3.org/2001/XInclude">
  472. ... <foo/>
  473. ... <xi:include href="doc/test.xml" />
  474. ... </doc>''')
  475. >>> tree = etree.parse(data)
  476. >>> tree.xinclude()
  477. >>> print(etree.tostring(tree.getroot()))
  478. <doc xmlns:xi="http://www.w3.org/2001/XInclude">
  479. <foo/>
  480. <a xml:base="doc/test.xml"/>
  481. </doc>
  482. Note that the ElementTree compatible ElementInclude_ module is also supported
  483. as ``lxml.ElementInclude``. It has the additional advantage of supporting
  484. custom `URL resolvers`_ at the Python level. The normal XInclude mechanism
  485. cannot deploy these. If you need ElementTree compatibility or custom
  486. resolvers, you have to stick to the external Python module.
  487. .. _ElementInclude: http://effbot.org/zone/element-xinclude.htm
  488. write_c14n on ElementTree
  489. -------------------------
  490. The lxml.etree.ElementTree class has a method write_c14n, which takes a file
  491. object as argument. This file object will receive an UTF-8 representation of
  492. the canonicalized form of the XML, following the W3C C14N recommendation. For
  493. example:
  494. .. sourcecode:: pycon
  495. >>> f = StringIO('<a><b/></a>')
  496. >>> tree = etree.parse(f)
  497. >>> f2 = StringIO()
  498. >>> tree.write_c14n(f2)
  499. >>> print(f2.getvalue().decode("utf-8"))
  500. <a><b></b></a>