aboutsummaryrefslogtreecommitdiffstats
path: root/doc/xml.html
blob: 03abc15578e61bc07fd1b0e53ab6dbb09ee977a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
   "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>The XML library for Gnome</title>
<meta name="GENERATOR" content="amaya V1.3b">
</head>
<body bgcolor="#ffffff">

<h1 align="center">The XML library for Gnome</h1>
<p>
This document describes the <a href="http://www.w3.org/XML/">XML</a> library
provideed in the <a href="http://www.gnome.org/">Gnome</a> framework. XML is a
standard to build tag based structured documents. The internal document
repesentation is as close as possible to the <a
href="http://www.w3.org/DOM/">DOM</a> interfaces.</p>

<h2>xml</h2>
<p>
XML is a standard for markup based structured documents, here is <a
name="example">an example</a>:</p>
<pre>&lt;?xml version="1.0"?>
&lt;EXAMPLE prop1="gnome is great" prop2="&amp;amp; linux too">
  &lt;head>
   &lt;title>Welcome to Gnome&lt;/title>
  &lt;/head>
  &lt;chapter>
   &lt;title>The Linux adventure&lt;/title>
   &lt;p>bla bla bla ...&lt;/p>
   &lt;image href="linus.gif"/>
   &lt;p>...&lt;/p>
  &lt;/chapter>
&lt;/EXAMPLE></pre>
<p>
The first line specify that it's an XML document and gives useful informations
about it's encoding. Then the document is a text format whose structure is
specified by tags between brackets. <strong>Each tag opened have to be
closed</strong> XML is pedantic about this, not that for example the image
tag has no content (just an attribute) and is closed by ending up the tag
with <code>/></code>.</p>

<h2>The tree output</h2>
<p>
The parser returns a tree built during the document analysis. The value
returned is an <strong>xmlDocPtr</strong> (i.e. a pointer to an
<strong>xmlDoc</strong> structure). This structure contains informations like
the file  name, the document type, and a <strong>root</strong> pointer which
is the root of the document (or more exactly the first child under the root
which is the document). The tree is made of <strong>xmlNode</strong>s, chained
in double linked lists of siblings and with childs&lt;->parent relationship.
An xmlNode can also carry properties (a chain of xmlAttr structures). An
attribute may have a value which is a list of TEXT or ENTITY_REF nodes.</p>
<p>
Here is an example (erroneous w.r.t. the XML spec since there should be only
one ELEMENT under the root):</p>
<p>
<img src="structure.gif" alt=" structure.gif "></p>
<p>
In the source package there is a small program (not installed by default)
called <strong>tester</strong> which parses XML files given as argument and
prints them back as parsed, this is useful to detect errors both in XML code
and in the XML parser itself. It has an option <strong>--debug</strong> which
prints the actual in-memory structure of the document, here is the result with
the <a href="#example">example</a> given before:</p>
<pre>DOCUMENT
version=1.0
standalone=true
  ELEMENT EXAMPLE
    ATTRIBUTE prop1
      TEXT
      content=gnome is great
    ATTRIBUTE prop2
      ENTITY_REF
      TEXT
      content= too
    ELEMENT head
      ELEMENT title
        TEXT
        content=Welcome to Gnome
    ELEMENT chapter
      ELEMENT title
        TEXT
        content=The Linux adventure
      ELEMENT p
        TEXT
        content=bla bla bla ...
      ELEMENT image
        ATTRIBUTE href
          TEXT
          content=linus.gif
      ELEMENT p
        TEXT
        content=...</pre>
<p>
This should be useful to learn the internal representation model.</p>

<h2>The XML library interfaces</h2>
<p>
This section is directly intended to help programmers getting bootstrapped
using the XML library from the C language. It doesn't intent to be extensive,
I hope the automatically generated docs will provide the completeness
required, but as a separated set of documents. The interfaces of the XML
library are by principle low level, there is nearly zero abstration. Those
interested in a higher level API should <a href="#DOM">look at DOM</a>
(unfortunately not completed).</p>

<h3>Invoking the parser</h3>
<p>
Usually, the first thing to do is to read an XML input, the parser accepts to
parse both memory mapped documents or direct files. The functions are defined
in "parser.h":</p>
<dl>
<dt><code>xmlDocPtr xmlParseMemory(char *buffer, int size);</code></dt>
<dd><p>
parse a zero terminated string containing the document</p>
</dd>
</dl>
<dl>
<dt><code>xmlDocPtr xmlParseFile(const char *filename);</code></dt>
<dd><p>
parse an XML document contained in a file (possibly compressed)</p>
</dd>
</dl>
<p>
This returns a pointer to the document structure (or NULL in case of
failure).</p>
<p>
A couple of comments can be made, first this mean that the parser is
memory-hungry, first to load the document in memory, second to build the tree.
Reading a document without building the tree will be possible in the future by
pluggin the code to the SAX interface (see SAX.c).</p>

<h3>Building a tree from scratch</h3>
<p>
The other way to get an XML tree in memory is by building it. Basically there
is a set of functions dedicated to building new elements, those are also
described in "tree.h", here is for example the piece of code producing the
example used before:</p>
<pre>    xmlDocPtr doc;
    xmlNodePtr tree, subtree;

    doc = xmlNewDoc("1.0");
    doc->root = xmlNewDocNode(doc, NULL, "EXAMPLE", NULL);
    xmlSetProp(doc->root, "prop1", "gnome is great");
    xmlSetProp(doc->root, "prop2", "&amp;linux; too");
    tree = xmlNewChild(doc->root, NULL, "head", NULL);
    subtree = xmlNewChild(tree, NULL, "title", "Welcome to Gnome");
    tree = xmlNewChild(doc->root, NULL, "chapter", NULL);
    subtree = xmlNewChild(tree, NULL, "title", "The Linux adventure");
    subtree = xmlNewChild(tree, NULL, "p", "bla bla bla ...");
    subtree = xmlNewChild(tree, NULL, "image", NULL);
    xmlSetProp(subtree, "href", "linus.gif");</pre>
<p>
Not really rocket science ...</p>

<h3>Traversing the tree</h3>
<p>
Basically by including "tree.h" your code has access to the internal structure
of all the element of the tree. The names should be somewhat simple like
<strong>parent</strong>, <strong>childs</strong>, <strong>next</strong>,
<strong>prev</strong>, <strong>properties</strong>, etc... For example still
with the previous example:</p>
<pre><code>doc->root->childs->childs</code></pre>
<p>
points to the title element,</p>
<pre>doc->root->childs->next->child->child</pre>
<p>
points to the text node containing the chapter titlle "The Linux adventure"
and</p>
<pre>doc->root->properties->next->val</pre>
<p>
points to the entity reference containing the value of "&amp;linux" at the
beginning of the second attribute of the root element "EXAMPLE".</p>

<h3>Modifying the tree</h3>
<p>
functions are provided to read and write the document content:</p>
<dl>
<dt><code>xmlAttrPtr xmlSetProp(xmlNodePtr node, const CHAR *name, const CHAR
*value);</code></dt>
<dd><p>
This set (or change) an attribute carried by an ELEMENT node the value can be
NULL</p>
</dd>
</dl>
<dl>
<dt><code>const CHAR *xmlGetProp(xmlNodePtr node, const CHAR
*name);</code></dt>
<dd><p>
This function returns a pointer to the property content, note that no extra
copy is made</p>
</dd>
</dl>
<p>
Two functions must be used to read an write the text associated to
elements:</p>
<dl>
<dt><code>xmlNodePtr xmlStringGetNodeList(xmlDocPtr doc, const CHAR
*value);</code></dt>
<dd><p>
This function takes an "external" string and convert it to one text node or
possibly to a list of entity and text nodes. All non-predefined entity
references like &amp;Gnome; will be stored internally as an entity node, hence
the result of the function may not be a single node.</p>
</dd>
</dl>
<dl>
<dt><code>CHAR *xmlNodeListGetString(xmlDocPtr doc, xmlNodePtr list, int
inLine);</code></dt>
<dd><p>
this is the dual function, which generate a new string containing the content
of the text and entity nodes. Note the extra argument inLine, if set to 1
instead of returning the &amp;Gnome; XML encoding in the string it will
substitute it with it's value say "GNU Network Object Model Environment". Set
it if you want to use the string for non XML usage like User Interface.</p>
</dd>
</dl>

<h3>Saving a tree</h3>
<p>
Basically 3 options are possible:</p>
<dl>
<dt><code>void xmlDocDumpMemory(xmlDocPtr cur, CHAR**mem, int
*size);</code></dt>
<dd><p>
returns a buffer where the document has been saved</p>
</dd>
</dl>
<dl>
<dt><code>extern void xmlDocDump(FILE *f, xmlDocPtr doc);</code></dt>
<dd><p>
dumps a buffer to an open file descriptor</p>
</dd>
</dl>
<dl>
<dt><code>int xmlSaveFile(const char *filename, xmlDocPtr cur);</code></dt>
<dd><p>
save the document ot a file. In that case the compression interface is
triggered if turned on</p>
</dd>
</dl>

<h3>Compression</h3>
<p>
The library handle transparently compression when doing file based accesses,
the level of compression on saves can be tuned either globally or individually
for one file:</p>
<dl>
<dt><code>int  xmlGetDocCompressMode (xmlDocPtr doc);</code></dt>
<dd><p>
Get the document compression ratio (0-9)</p>
</dd>
</dl>
<dl>
<dt><code>void xmlSetDocCompressMode (xmlDocPtr doc, int mode);</code></dt>
<dd><p>
Set the document compression ratio</p>
</dd>
</dl>
<dl>
<dt><code>int  xmlGetCompressMode(void);</code></dt>
<dd><p>
Get the default compression ratio</p>
</dd>
</dl>
<dl>
<dt><code>void xmlSetCompressMode(int mode);</code></dt>
<dd><p>
set the default compression ratio</p>
</dd>
</dl>

<h2><a name="DOM">DOM Principles</a></h2>
<p>
<a href="http://www.w3.org/DOM/">DOM</a> stands for the <em>Document Object
Model</em> this is an API for accessing XML or HTML structured documents.
Native support for DOM in Gnome is on the way (module gnome-dom), and it will
be based on gnome-xml. This will be a far cleaner interface to manipulate XML
files within Gnome since it won't expose the internal structure. DOM defiles a
set of IDL (or Java) interfaces allowing to traverse and manipulate a
document. The DOM library will allow accessing and modifying "live" documents
presents on other programs like this:</p>
<p>
<img src="DOM.gif" alt=" DOM.gif "></p>
<p>
This should help greatly doing things like modifying a gnumeric spreadsheet
embedded in a GWP document for example.</p>

<h3><a name="Example">A real example</a></h3>
<p>
Here is a real size example, where the actual content of the application data
is not kept in the DOM tree but uses internal structures. It is based on
a proposal to keep a database of jobs related to Gnome, with an XML based
storage structure. Here is an <a href="gjobs.xml">XML encoded jobs base</a>:
<pre>
&lt;?xml version="1.0"?>
&lt;gjob:Helping xmlns:gjob="http://www.gnome.org/some-location">
  &lt;gjob:Jobs>

    &lt;gjob:Job>
      &lt;gjob:Project ID="3"/>
      &lt;gjob:Application>GBackup&lt;/gjob:Application>
      &lt;gjob:Category>Development&lt;/gjob:Category>

      &lt;gjob:Update>
	&lt;gjob:Status>Open&lt;/gjob:Status>
	&lt;gjob:Modified>Mon, 07 Jun 1999 20:27:45 -0400 MET DST&lt;/gjob:Modified>
        &lt;gjob:Salary>USD 0.00&lt;/gjob:Salary>
      &lt;/gjob:Update>

      &lt;gjob:Developers>
        &lt;gjob:Developer>
        &lt;/gjob:Developer>
      &lt;/gjob:Developers>

      &lt;gjob:Contact>
        &lt;gjob:Person>Nathan Clemons&lt;/gjob:Person>
	&lt;gjob:Email>nathan@windsofstorm.net&lt;/gjob:Email>
        &lt;gjob:Company>
	&lt;/gjob:Company>
        &lt;gjob:Organisation>
	&lt;/gjob:Organisation>
        &lt;gjob:Webpage>
	&lt;/gjob:Webpage>
	&lt;gjob:Snailmail>
	&lt;/gjob:Snailmail>
	&lt;gjob:Phone>
	&lt;/gjob:Phone>
      &lt;/gjob:Contact>

      &lt;gjob:Requirements>
      The program should be released as free software, under the GPL.
      &lt;/gjob:Requirements>

      &lt;gjob:Skills>
      &lt;/gjob:Skills>

      &lt;gjob:Details>
      A GNOME based system that will allow a superuser to configure 
      compressed and uncompressed files and/or file systems to be backed 
      up with a supported media in the system.  This should be able to 
      perform via find commands generating a list of files that are passed 
      to tar, dd, cpio, cp, gzip, etc., to be directed to the tape machine 
      or via operations performed on the filesystem itself. Email 
      notification and GUI status display very important.
      &lt;/gjob:Details>

    &lt;/gjob:Job>

  &lt;/gjob:Jobs>
&lt;/gjob:Helping>

</pre>
<p>
While loading the XML file into an internal DOM tree is a matter of calling
only a couple of functions, browsing the tree to gather the informations
and generate the internals structures is harder, and more error prone. 
</p>
<p>
The suggested principle is to be tolerant with respect to the input
structure. For example the ordering of the attributes is not significant,
Cthe XML specification is clear about it. It's also usually a good idea
to not be dependant of the orders of the childs of a given node, unless it
really makes things harder. Here is some code to parse the informations
for a person:
</p>
<pre>
/*
 * A person record
 */
typedef struct person {
    char *name;
    char *email;
    char *company;
    char *organisation;
    char *smail;
    char *webPage;
    char *phone;
} person, *personPtr;

/*
 * And the code needed to parse it
 */
personPtr parsePerson(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur) {
    personPtr ret = NULL;

DEBUG("parsePerson\n");
    /*
     * allocate the struct
     */
    ret = (personPtr) malloc(sizeof(person));
    if (ret == NULL) {
        fprintf(stderr,"out of memory\n");
	return(NULL);
    }
    memset(ret, 0, sizeof(person));

    /* We don't care what the top level element name is */
    cur = cur->childs;
    while (cur != NULL) {
        if ((!strcmp(cur->name, "Person")) &amp;&amp; (cur->ns == ns))
	    ret->name = xmlNodeListGetString(doc, cur->childs, 1);
        if ((!strcmp(cur->name, "Email")) &amp;&amp; (cur->ns == ns))
	    ret->email = xmlNodeListGetString(doc, cur->childs, 1);
	cur = cur->next;
    }

    return(ret);
}
</pre>
<p>
Here is a couple of things to notice:</p>
<ul>
<li> Usually a recursive parsing style is the more convenient one,
XML data being by nature subject to repetitive constructs and usualy exibit
highly stuctured patterns.
<li> The two arguments of type <em>xmlDocPtr</em> and <em>xmlNsPtr</em>, i.e.
the pointer to the global XML document and the namespace reserved to the
application. Document wide information are needed for example to decode
entities and it's a good coding practice to define a namespace for your
application set of data and test that the element and attributes you're
analyzing actually pertains to your application space. This is done by a simple
equality test (cur->ns == ns).
<li> To retrieve text and attributes value, it is suggested to use
the function <em>xmlNodeListGetString</em> to gather all the text and
entity reference nodes generated by the DOM output and produce an
single text string.
</ul>
<p>
Here is another piece of code used to parse another level of the structure:
</p>
<pre>
/*
 * a Description for a Job
 */
typedef struct job {
    char *projectID;
    char *application;
    char *category;
    personPtr contact;
    int nbDevelopers;
    personPtr developers[100]; /* using dynamic alloc is left as an exercise */
} job, *jobPtr;

/*
 * And the code needed to parse it
 */
jobPtr parseJob(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur) {
    jobPtr ret = NULL;

DEBUG("parseJob\n");
    /*
     * allocate the struct
     */
    ret = (jobPtr) malloc(sizeof(job));
    if (ret == NULL) {
        fprintf(stderr,"out of memory\n");
	return(NULL);
    }
    memset(ret, 0, sizeof(job));

    /* We don't care what the top level element name is */
    cur = cur->childs;
    while (cur != NULL) {
        
        if ((!strcmp(cur->name, "Project")) &amp;&amp; (cur->ns == ns)) {
	    ret->projectID = xmlGetProp(cur, "ID");
	    if (ret->projectID == NULL) {
		fprintf(stderr, "Project has no ID\n");
	    }
	}
        if ((!strcmp(cur->name, "Application")) &amp;&amp; (cur->ns == ns))
	    ret->application = xmlNodeListGetString(doc, cur->childs, 1);
        if ((!strcmp(cur->name, "Category")) &amp;&amp; (cur->ns == ns))
	    ret->category = xmlNodeListGetString(doc, cur->childs, 1);
        if ((!strcmp(cur->name, "Contact")) &amp;&amp; (cur->ns == ns))
	    ret->contact = parsePerson(doc, ns, cur);
	cur = cur->next;
    }

    return(ret);
}
</pre>
<p>
One can notice that once used to it, writing this kind of code
is quite simple, but boring. Ultimately, it could be possble to write
stubbers taking either C data structure definitions, a set of XML examples
or an XML DTD and produce the code needed to import and export the
content between C data and XML storage. This is left as an exercise to
the reader :-)</p>
<p>
Feel free to use <a href="gjobread.c">the code for the full C parsing
example</a> as a template,

<a href="mailto:Daniel.Veillard@w3.org">Daniel Veillard</a>
</body>
</html>