xmlreader.html 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  2. "http://www.w3.org/TR/html4/loose.dtd">
  3. <html>
  4. <head>
  5. <meta http-equiv="Content-Type" content="text/html">
  6. <style type="text/css"></style>
  7. <!--
  8. TD {font-family: Verdana,Arial,Helvetica}
  9. BODY {font-family: Verdana,Arial,Helvetica; margin-top: 2em; margin-left: 0em; margin-right: 0em}
  10. H1 {font-family: Verdana,Arial,Helvetica}
  11. H2 {font-family: Verdana,Arial,Helvetica}
  12. H3 {font-family: Verdana,Arial,Helvetica}
  13. A:link, A:visited, A:active { text-decoration: underline }
  14. </style>
  15. -->
  16. <title>Libxml2 XmlTextReader Interface tutorial</title>
  17. </head>
  18. <body bgcolor="#fffacd" text="#000000">
  19. <h1 align="center">Libxml2 XmlTextReader Interface tutorial</h1>
  20. <p></p>
  21. <p>This document describes the use of the XmlTextReader streaming API added
  22. to libxml2 in version 2.5.0 . This API is closely modeled after the <a
  23. href="http://dotgnu.org/pnetlib-doc/System/Xml/XmlTextReader.html">XmlTextReader</a>
  24. and <a
  25. href="http://dotgnu.org/pnetlib-doc/System/Xml/XmlReader.html">XmlReader</a>
  26. classes of the C# language.</p>
  27. <p>This tutorial will present the key points of this API, and working
  28. examples using both C and the Python bindings:</p>
  29. <p>Table of content:</p>
  30. <ul>
  31. <li><a href="#Introducti">Introduction: why a new API</a></li>
  32. <li><a href="#Walking">Walking a simple tree</a></li>
  33. <li><a href="#Extracting">Extracting informations for the current
  34. node</a></li>
  35. <li><a href="#Extracting1">Extracting informations for the
  36. attributes</a></li>
  37. <li><a href="#Validating">Validating a document</a></li>
  38. <li><a href="#Entities">Entities substitution</a></li>
  39. <li><a href="#L1142">Relax-NG Validation</a></li>
  40. <li><a href="#Mixing">Mixing the reader and tree or XPath
  41. operations</a></li>
  42. </ul>
  43. <p></p>
  44. <h2><a name="Introducti">Introduction: why a new API</a></h2>
  45. <p>Libxml2 <a href="http://xmlsoft.org/html/libxml-tree.html">main API is
  46. tree based</a>, where the parsing operation results in a document loaded
  47. completely in memory, and expose it as a tree of nodes all availble at the
  48. same time. This is very simple and quite powerful, but has the major
  49. limitation that the size of the document that can be hamdled is limited by
  50. the size of the memory available. Libxml2 also provide a <a
  51. href="http://www.saxproject.org/">SAX</a> based API, but that version was
  52. designed upon one of the early <a
  53. href="http://www.jclark.com/xml/expat.html">expat</a> version of SAX, SAX is
  54. also not formally defined for C. SAX basically work by registering callbacks
  55. which are called directly by the parser as it progresses through the document
  56. streams. The problem is that this programming model is relatively complex,
  57. not well standardized, cannot provide validation directly, makes entity,
  58. namespace and base processing relatively hard.</p>
  59. <p>The <a
  60. href="http://dotgnu.org/pnetlib-doc/System/Xml/XmlTextReader.html">XmlTextReader
  61. API from C#</a> provides a far simpler programming model. The API acts as a
  62. cursor going forward on the document stream and stopping at each node in the
  63. way. The user's code keeps control of the progress and simply calls a
  64. Read() function repeatedly to progress to each node in sequence in document
  65. order. There is direct support for namespaces, xml:base, entity handling and
  66. adding DTD validation on top of it was relatively simple. This API is really
  67. close to the <a href="http://www.w3.org/TR/DOM-Level-2-Core/">DOM Core
  68. specification</a> This provides a far more standard, easy to use and powerful
  69. API than the existing SAX. Moreover integrating extension features based on
  70. the tree seems relatively easy.</p>
  71. <p>In a nutshell the XmlTextReader API provides a simpler, more standard and
  72. more extensible interface to handle large documents than the existing SAX
  73. version.</p>
  74. <h2><a name="Walking">Walking a simple tree</a></h2>
  75. <p>Basically the XmlTextReader API is a forward only tree walking interface.
  76. The basic steps are:</p>
  77. <ol>
  78. <li>prepare a reader context operating on some input</li>
  79. <li>run a loop iterating over all nodes in the document</li>
  80. <li>free up the reader context</li>
  81. </ol>
  82. <p>Here is a basic C sample doing this:</p>
  83. <pre>#include &lt;libxml/xmlreader.h&gt;
  84. void processNode(xmlTextReaderPtr reader) {
  85. /* handling of a node in the tree */
  86. }
  87. int streamFile(char *filename) {
  88. xmlTextReaderPtr reader;
  89. int ret;
  90. reader = xmlNewTextReaderFilename(filename);
  91. if (reader != NULL) {
  92. ret = xmlTextReaderRead(reader);
  93. while (ret == 1) {
  94. processNode(reader);
  95. ret = xmlTextReaderRead(reader);
  96. }
  97. xmlFreeTextReader(reader);
  98. if (ret != 0) {
  99. printf("%s : failed to parse\n", filename);
  100. }
  101. } else {
  102. printf("Unable to open %s\n", filename);
  103. }
  104. }</pre>
  105. <p>A few things to notice:</p>
  106. <ul>
  107. <li>the include file needed : <code>libxml/xmlreader.h</code></li>
  108. <li>the creation of the reader using a filename</li>
  109. <li>the repeated call to xmlTextReaderRead() and how any return value
  110. different from 1 should stop the loop</li>
  111. <li>that a negative return means a parsing error</li>
  112. <li>how xmlFreeTextReader() should be used to free up the resources used by
  113. the reader.</li>
  114. </ul>
  115. <p>Here is similar code in python for exactly the same processing:</p>
  116. <pre>import libxml2
  117. def processNode(reader):
  118. pass
  119. def streamFile(filename):
  120. try:
  121. reader = libxml2.newTextReaderFilename(filename)
  122. except:
  123. print "unable to open %s" % (filename)
  124. return
  125. ret = reader.Read()
  126. while ret == 1:
  127. processNode(reader)
  128. ret = reader.Read()
  129. if ret != 0:
  130. print "%s : failed to parse" % (filename)</pre>
  131. <p>The only things worth adding are that the <a
  132. href="http://dotgnu.org/pnetlib-doc/System/Xml/XmlTextReader.html">xmlTextReader
  133. is abstracted as a class like in C#</a> with the same method names (but the
  134. properties are currently accessed with methods) and that one doesn't need to
  135. free the reader at the end of the processing. It will get garbage collected
  136. once all references have disapeared.</p>
  137. <h2><a name="Extracting">Extracting information for the current node</a></h2>
  138. <p>So far the example code did not indicate how information was extracted
  139. from the reader. It was abstrated as a call to the processNode() routine,
  140. with the reader as the argument. At each invocation, the parser is stopped on
  141. a given node and the reader can be used to query those node properties. Each
  142. <em>Property</em> is available at the C level as a function taking a single
  143. xmlTextReaderPtr argument whose name is
  144. <code>xmlTextReader</code><em>Property</em> , if the return type is an
  145. <code>xmlChar *</code> string then it must be deallocated with
  146. <code>xmlFree()</code> to avoid leaks. For the Python interface, there is a
  147. <em>Property</em> method to the reader class that can be called on the
  148. instance. The list of the properties is based on the <a
  149. href="http://dotgnu.org/pnetlib-doc/System/Xml/XmlTextReader.html">C#
  150. XmlTextReader class</a> set of properties and methods:</p>
  151. <ul>
  152. <li><em>NodeType</em>: The node type, 1 for start element, 15 for end of
  153. element, 2 for attributes, 3 for text nodes, 4 for CData sections, 5 for
  154. entity references, 6 for entity declarations, 7 for PIs, 8 for comments,
  155. 9 for the document nodes, 10 for DTD/Doctype nodes, 11 for document
  156. fragment and 12 for notation nodes.</li>
  157. <li><em>Name</em>: the <a
  158. href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
  159. name</a> of the node, equal to (<em>Prefix</em>:)<em>LocalName</em>.</li>
  160. <li><em>LocalName</em>: the <a
  161. href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local name</a> of
  162. the node.</li>
  163. <li><em>Prefix</em>: a shorthand reference to the <a
  164. href="http://www.w3.org/TR/REC-xml-names/">namespace</a> associated with
  165. the node.</li>
  166. <li><em>NamespaceUri</em>: the URI defining the <a
  167. href="http://www.w3.org/TR/REC-xml-names/">namespace</a> associated with
  168. the node.</li>
  169. <li><em>BaseUri:</em> the base URI of the node. See the <a
  170. href="http://www.w3.org/TR/xmlbase/">XML Base W3C specification</a>.</li>
  171. <li><em>Depth:</em> the depth of the node in the tree, starts at 0 for the
  172. root node.</li>
  173. <li><em>HasAttributes</em>: whether the node has attributes.</li>
  174. <li><em>HasValue</em>: whether the node can have a text value.</li>
  175. <li><em>Value</em>: provides the text value of the node if present.</li>
  176. <li><em>IsDefault</em>: whether an Attribute node was generated from the
  177. default value defined in the DTD or schema (<em>unsupported
  178. yet</em>).</li>
  179. <li><em>XmlLang</em>: the <a
  180. href="http://www.w3.org/TR/REC-xml#sec-lang-tag">xml:lang</a> scope
  181. within which the node resides.</li>
  182. <li><em>IsEmptyElement</em>: check if the current node is empty, this is a
  183. bit bizarre in the sense that <code>&lt;a/&gt;</code> will be considered
  184. empty while <code>&lt;a&gt;&lt;/a&gt;</code> will not.</li>
  185. <li><em>AttributeCount</em>: provides the number of attributes of the
  186. current node.</li>
  187. </ul>
  188. <p>Let's look first at a small example to get this in practice by redefining
  189. the processNode() function in the Python example:</p>
  190. <pre>def processNode(reader):
  191. print "%d %d %s %d" % (reader.Depth(), reader.NodeType(),
  192. reader.Name(), reader.IsEmptyElement())</pre>
  193. <p>and look at the result of calling streamFile("tst.xml") for various
  194. content of the XML test file.</p>
  195. <p>For the minimal document "<code>&lt;doc/&gt;</code>" we get:</p>
  196. <pre>0 1 doc 1</pre>
  197. <p>Only one node is found, its depth is 0, type 1 indicate an element start,
  198. of name "doc" and it is empty. Trying now with
  199. "<code>&lt;doc&gt;&lt;/doc&gt;</code>" instead leads to:</p>
  200. <pre>0 1 doc 0
  201. 0 15 doc 0</pre>
  202. <p>The document root node is not flagged as empty anymore and both a start
  203. and an end of element are detected. The following document shows how
  204. character data are reported:</p>
  205. <pre>&lt;doc&gt;&lt;a/&gt;&lt;b&gt;some text&lt;/b&gt;
  206. &lt;c/&gt;&lt;/doc&gt;</pre>
  207. <p>We modifying the processNode() function to also report the node Value:</p>
  208. <pre>def processNode(reader):
  209. print "%d %d %s %d %s" % (reader.Depth(), reader.NodeType(),
  210. reader.Name(), reader.IsEmptyElement(),
  211. reader.Value())</pre>
  212. <p>The result of the test is:</p>
  213. <pre>0 1 doc 0 None
  214. 1 1 a 1 None
  215. 1 1 b 0 None
  216. 2 3 #text 0 some text
  217. 1 15 b 0 None
  218. 1 3 #text 0
  219. 1 1 c 1 None
  220. 0 15 doc 0 None</pre>
  221. <p>There are a few things to note:</p>
  222. <ul>
  223. <li>the increase of the depth value (first row) as children nodes are
  224. explored</li>
  225. <li>the text node child of the b element, of type 3 and its content</li>
  226. <li>the text node containing the line return between elements b and c</li>
  227. <li>that elements have the Value None (or NULL in C)</li>
  228. </ul>
  229. <p>The equivalent routine for <code>processNode()</code> as used by
  230. <code>xmllint --stream --debug</code> is the following and can be found in
  231. the xmllint.c module in the source distribution:</p>
  232. <pre>static void processNode(xmlTextReaderPtr reader) {
  233. xmlChar *name, *value;
  234. name = xmlTextReaderName(reader);
  235. if (name == NULL)
  236. name = xmlStrdup(BAD_CAST "--");
  237. value = xmlTextReaderValue(reader);
  238. printf("%d %d %s %d",
  239. xmlTextReaderDepth(reader),
  240. xmlTextReaderNodeType(reader),
  241. name,
  242. xmlTextReaderIsEmptyElement(reader));
  243. xmlFree(name);
  244. if (value == NULL)
  245. printf("\n");
  246. else {
  247. printf(" %s\n", value);
  248. xmlFree(value);
  249. }
  250. }</pre>
  251. <h2><a name="Extracting1">Extracting information for the attributes</a></h2>
  252. <p>The previous examples don't indicate how attributes are processed. The
  253. simple test "<code>&lt;doc a="b"/&gt;</code>" provides the following
  254. result:</p>
  255. <pre>0 1 doc 1 None</pre>
  256. <p>This proves that attribute nodes are not traversed by default. The
  257. <em>HasAttributes</em> property allow to detect their presence. To check
  258. their content the API has special instructions. Basically two kinds of operations
  259. are possible:</p>
  260. <ol>
  261. <li>to move the reader to the attribute nodes of the current element, in
  262. that case the cursor is positionned on the attribute node</li>
  263. <li>to directly query the element node for the attribute value</li>
  264. </ol>
  265. <p>In both case the attribute can be designed either by its position in the
  266. list of attribute (<em>MoveToAttributeNo</em> or <em>GetAttributeNo</em>) or
  267. by their name (and namespace):</p>
  268. <ul>
  269. <li><em>GetAttributeNo</em>(no): provides the value of the attribute with
  270. the specified index no relative to the containing element.</li>
  271. <li><em>GetAttribute</em>(name): provides the value of the attribute with
  272. the specified qualified name.</li>
  273. <li>GetAttributeNs(localName, namespaceURI): provides the value of the
  274. attribute with the specified local name and namespace URI.</li>
  275. <li><em>MoveToAttributeNo</em>(no): moves the position of the current
  276. instance to the attribute with the specified index relative to the
  277. containing element.</li>
  278. <li><em>MoveToAttribute</em>(name): moves the position of the current
  279. instance to the attribute with the specified qualified name.</li>
  280. <li><em>MoveToAttributeNs</em>(localName, namespaceURI): moves the position
  281. of the current instance to the attribute with the specified local name
  282. and namespace URI.</li>
  283. <li><em>MoveToFirstAttribute</em>: moves the position of the current
  284. instance to the first attribute associated with the current node.</li>
  285. <li><em>MoveToNextAttribute</em>: moves the position of the current
  286. instance to the next attribute associated with the current node.</li>
  287. <li><em>MoveToElement</em>: moves the position of the current instance to
  288. the node that contains the current Attribute node.</li>
  289. </ul>
  290. <p>After modifying the processNode() function to show attributes:</p>
  291. <pre>def processNode(reader):
  292. print "%d %d %s %d %s" % (reader.Depth(), reader.NodeType(),
  293. reader.Name(), reader.IsEmptyElement(),
  294. reader.Value())
  295. if reader.NodeType() == 1: # Element
  296. while reader.MoveToNextAttribute():
  297. print "-- %d %d (%s) [%s]" % (reader.Depth(), reader.NodeType(),
  298. reader.Name(),reader.Value())</pre>
  299. <p>The output for the same input document reflects the attribute:</p>
  300. <pre>0 1 doc 1 None
  301. -- 1 2 (a) [b]</pre>
  302. <p>There are a couple of things to note on the attribute processing:</p>
  303. <ul>
  304. <li>Their depth is the one of the carrying element plus one.</li>
  305. <li>Namespace declarations are seen as attributes, as in DOM.</li>
  306. </ul>
  307. <h2><a name="Validating">Validating a document</a></h2>
  308. <p>Libxml2 implementation adds some extra features on top of the XmlTextReader
  309. API. The main one is the ability to DTD validate the parsed document
  310. progressively. This is simply the activation of the associated feature of the
  311. parser used by the reader structure. There are a few options available
  312. defined as the enum xmlParserProperties in the libxml/xmlreader.h header
  313. file:</p>
  314. <ul>
  315. <li>XML_PARSER_LOADDTD: force loading the DTD (without validating)</li>
  316. <li>XML_PARSER_DEFAULTATTRS: force attribute defaulting (this also imply
  317. loading the DTD)</li>
  318. <li>XML_PARSER_VALIDATE: activate DTD validation (this also imply loading
  319. the DTD)</li>
  320. <li>XML_PARSER_SUBST_ENTITIES: substitute entities on the fly, entity
  321. reference nodes are not generated and are replaced by their expanded
  322. content.</li>
  323. <li>more settings might be added, those were the one available at the 2.5.0
  324. release...</li>
  325. </ul>
  326. <p>The GetParserProp() and SetParserProp() methods can then be used to get
  327. and set the values of those parser properties of the reader. For example</p>
  328. <pre>def parseAndValidate(file):
  329. reader = libxml2.newTextReaderFilename(file)
  330. reader.SetParserProp(libxml2.PARSER_VALIDATE, 1)
  331. ret = reader.Read()
  332. while ret == 1:
  333. ret = reader.Read()
  334. if ret != 0:
  335. print "Error parsing and validating %s" % (file)</pre>
  336. <p>This routine will parse and validate the file. Error messages can be
  337. captured by registering an error handler. See python/tests/reader2.py for
  338. more complete Python examples. At the C level the equivalent call to cativate
  339. the validation feature is just:</p>
  340. <pre>ret = xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1)</pre>
  341. <p>and a return value of 0 indicates success.</p>
  342. <h2><a name="Entities">Entities substitution</a></h2>
  343. <p>By default the xmlReader will report entities as such and not replace them
  344. with their content. This default behaviour can however be overriden using:</p>
  345. <p><code>reader.SetParserProp(libxml2.PARSER_SUBST_ENTITIES,1)</code></p>
  346. <h2><a name="L1142">Relax-NG Validation</a></h2>
  347. <p style="font-size: 10pt">Introduced in version 2.5.7</p>
  348. <p>Libxml2 can now validate the document being read using the xmlReader using
  349. Relax-NG schemas. While the Relax NG validator can't always work in a
  350. streamable mode, only subsets which cannot be reduced to regular expressions
  351. need to have their subtree expanded for validation. In practice it means
  352. that, unless the schemas for the top level element content is not expressable
  353. as a regexp, only chunk of the document needs to be parsed while
  354. validating.</p>
  355. <p>The steps to do so are:</p>
  356. <ul>
  357. <li>create a reader working on a document as usual</li>
  358. <li>before any call to read associate it to a Relax NG schemas, either the
  359. preparsed schemas or the URL to the schemas to use</li>
  360. <li>errors will be reported the usual way, and the validity status can be
  361. obtained using the IsValid() interface of the reader like for DTDs.</li>
  362. </ul>
  363. <p>Example, assuming the reader has already being created and that the schema
  364. string contains the Relax-NG schemas:</p>
  365. <pre><code>rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema))<br>
  366. rngs = rngp.relaxNGParse()<br>
  367. reader.RelaxNGSetSchema(rngs)<br>
  368. ret = reader.Read()<br>
  369. while ret == 1:<br>
  370. ret = reader.Read()<br>
  371. if ret != 0:<br>
  372. print "Error parsing the document"<br>
  373. if reader.IsValid() != 1:<br>
  374. print "Document failed to validate"</code><br>
  375. </pre>
  376. <p>See <code>reader6.py</code> in the sources or documentation for a complete
  377. example.</p>
  378. <h2><a name="Mixing">Mixing the reader and tree or XPath operations</a></h2>
  379. <p style="font-size: 10pt">Introduced in version 2.5.7</p>
  380. <p>While the reader is a streaming interface, its underlying implementation
  381. is based on the DOM builder of libxml2. As a result it is relatively simple
  382. to mix operations based on both models under some constraints. To do so the
  383. reader has an Expand() operation allowing to grow the subtree under the
  384. current node. It returns a pointer to a standard node which can be
  385. manipulated in the usual ways. The node will get all its ancestors and the
  386. full subtree available. Usual operations like XPath queries can be used on
  387. that reduced view of the document. Here is an example extracted from
  388. reader5.py in the sources which extract and prints the bibliography for the
  389. "Dragon" compiler book from the XML 1.0 recommendation:</p>
  390. <pre>f = open('../../test/valid/REC-xml-19980210.xml')
  391. input = libxml2.inputBuffer(f)
  392. reader = input.newTextReader("REC")
  393. res=""
  394. while reader.Read():
  395. while reader.Name() == 'bibl':
  396. node = reader.Expand() # expand the subtree
  397. if node.xpathEval("@id = 'Aho'"): # use XPath on it
  398. res = res + node.serialize()
  399. if reader.Next() != 1: # skip the subtree
  400. break;</pre>
  401. <p>Note, however that the node instance returned by the Expand() call is only
  402. valid until the next Read() operation. The Expand() operation does not
  403. affects the Read() ones, however usually once processed the full subtree is
  404. not useful anymore, and the Next() operation allows to skip it completely and
  405. process to the successor or return 0 if the document end is reached.</p>
  406. <p><a href="mailto:xml@gnome.org">Daniel Veillard</a></p>
  407. <p>$Id$</p>
  408. <p></p>
  409. </body>
  410. </html>