sax.txt 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. Sax support
  2. ===========
  3. In this document we'll describe lxml's SAX support. lxml has support for
  4. producing SAX events for an ElementTree or Element. lxml can also turn SAX
  5. events into an ElementTree. The SAX API used by lxml is compatible with that
  6. in the Python core (xml.sax), so is useful for interfacing lxml with code that
  7. uses the Python core SAX facilities.
  8. .. contents::
  9. ..
  10. 1 Building a tree from SAX events
  11. 2 Producing SAX events from an ElementTree or Element
  12. 3 Interfacing with pulldom/minidom
  13. ..
  14. >>> try: from StringIO import StringIO
  15. ... except ImportError:
  16. ... from io import BytesIO
  17. ... def StringIO(s):
  18. ... if isinstance(s, str): s = s.encode("UTF-8")
  19. ... return BytesIO(s)
  20. Building a tree from SAX events
  21. -------------------------------
  22. First of all, lxml has support for building a new tree given SAX events. To
  23. do this, we use the special SAX content handler defined by lxml named
  24. ``lxml.sax.ElementTreeContentHandler``:
  25. .. sourcecode:: pycon
  26. >>> import lxml.sax
  27. >>> handler = lxml.sax.ElementTreeContentHandler()
  28. Now let's fire some SAX events at it:
  29. .. sourcecode:: pycon
  30. >>> handler.startElementNS((None, 'a'), 'a', {})
  31. >>> handler.startElementNS((None, 'b'), 'b', {(None, 'foo'): 'bar'})
  32. >>> handler.characters('Hello world')
  33. >>> handler.endElementNS((None, 'b'), 'b')
  34. >>> handler.endElementNS((None, 'a'), 'a')
  35. This constructs an equivalent tree. You can access it through the ``etree``
  36. property of the handler:
  37. .. sourcecode:: pycon
  38. >>> tree = handler.etree
  39. >>> lxml.etree.tostring(tree.getroot())
  40. b'<a><b foo="bar">Hello world</b></a>'
  41. By passing a ``makeelement`` function the constructor of
  42. ``ElementTreeContentHandler``, e.g. the one of a parser you configured, you
  43. can determine which element class lookup scheme should be used.
  44. Producing SAX events from an ElementTree or Element
  45. ---------------------------------------------------
  46. Let's make a tree we can generate SAX events for:
  47. .. sourcecode:: pycon
  48. >>> f = StringIO('<a><b>Text</b></a>')
  49. >>> tree = lxml.etree.parse(f)
  50. To see whether the correct SAX events are produced, we'll write a custom
  51. content handler.:
  52. .. sourcecode:: pycon
  53. >>> from xml.sax.handler import ContentHandler
  54. >>> class MyContentHandler(ContentHandler):
  55. ... def __init__(self):
  56. ... self.a_amount = 0
  57. ... self.b_amount = 0
  58. ... self.text = None
  59. ...
  60. ... def startElementNS(self, name, qname, attributes):
  61. ... uri, localname = name
  62. ... if localname == 'a':
  63. ... self.a_amount += 1
  64. ... if localname == 'b':
  65. ... self.b_amount += 1
  66. ...
  67. ... def characters(self, data):
  68. ... self.text = data
  69. Note that it only defines the startElementNS() method and not startElement().
  70. The SAX event generator in lxml.sax currently only supports namespace-aware
  71. processing.
  72. To test the content handler, we can produce SAX events from the tree:
  73. .. sourcecode:: pycon
  74. >>> handler = MyContentHandler()
  75. >>> lxml.sax.saxify(tree, handler)
  76. This is what we expect:
  77. .. sourcecode:: pycon
  78. >>> handler.a_amount
  79. 1
  80. >>> handler.b_amount
  81. 1
  82. >>> handler.text
  83. 'Text'
  84. Interfacing with pulldom/minidom
  85. --------------------------------
  86. lxml.sax is a simple way to interface with the standard XML support in the
  87. Python library. Note, however, that this is a one-way solution, as Python's
  88. DOM implementation connot generate SAX events from a DOM tree.
  89. You can use xml.dom.pulldom to build a minidom from lxml:
  90. .. sourcecode:: pycon
  91. >>> from xml.dom.pulldom import SAX2DOM
  92. >>> handler = SAX2DOM()
  93. >>> lxml.sax.saxify(tree, handler)
  94. PullDOM makes the result available through the ``document`` attribute:
  95. .. sourcecode:: pycon
  96. >>> dom = handler.document
  97. >>> print(dom.firstChild.localName)
  98. a