]>
Libxml Tutorial John Fleck 2002 John Fleck 1 June 4,2002 2 June 12, 2002 3 Aug. 31, 2002 Libxml is a freely licensed C language library for handling XML, portable across a large number of platforms. This tutorial provides examples of its basic functions. Introduction Libxml is a C language library implementing functions for reading, creating and manipulating XML data. This tutorial provides example code and explanations of its basic functionality. Libxml and more details about its use are available on the project home page. Included there is complete API documentation. This tutorial is not meant to substitute for that complete documentation, but to illustrate the functions needed to use the library to perform basic operations. The tutorial is based on a simple XML application I use for articles I write. The format includes metadata and the body of the article. The example code in this tutorial demonstrates how to: Parse the document. Extract the text within a specified element. Add an element and its content. Add an attribute. Extract the value of an attribute. Full code for the examples is included in the appendices. Data Types Libxml declares a number of datatypes we will encounter repeatedly, hiding the messy stuff so you do not have to deal with it unless you have some specific need. xmlChar A basic replacement for char, a byte in a UTF-8 encoded string. xmlDoc A structure containing the tree created by a parsed doc. xmlDocPtr is a pointer to the structure. xmlNodePtr and xmlNode A structure containing a single node. xmlNodePtr is a pointer to the structure, and is used in traversing the document tree. Parsing the file Parsing the file requires only the name of the file and a single function call, plus error checking. Full code: xmlDocPtr doc; xmlNodePtr cur; doc = xmlParseFile(docname); if (doc == NULL ) { fprintf(stderr,"Document not parsed successfully. \n"); xmlFreeDoc(doc); return; } cur = xmlDocGetRootElement(doc); if (cur == NULL) { fprintf(stderr,"empty document\n"); xmlFreeDoc(doc); return; } if (xmlStrcmp(cur->name, (const xmlChar *) "story")) { fprintf(stderr,"document of the wrong type, root node != story"); xmlFreeDoc(doc); return; } Declare the pointer that will point to your parsed document. Declare a node pointer (you'll need this in order to interact with individual nodes). Check to see that the document was successfully parsed. Retrieve the document's root element. Check to make sure the document actually contains something. In our case, we need to make sure the document is the right type. "story" is the root type of my documents. Retrieving Element Content Retrieving the content of an element involves traversing the document tree until you find what you are looking for. In this case, we are looking for an element called "keyword" contained within element called "story". The process to find the node we are interested in involves tediously walking the tree. We assume you already have an xmlDocPtr called doc and an xmlNodPtr called cur. cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"storyinfo"))){ parseStory (doc, cur); } cur = cur->next; } Get the first child node of cur. At this point, cur points at the document root, which is the element "story". This loop iterates through the elements that are children of "story", looking for one called "storyinfo". That is the element that will contain the "keywords" we are looking for. It uses the libxml string comparison function, xmlStrcmp. If there is a match, it calls the function parseStory. void parseStory (xmlDocPtr doc, xmlNodePtr cur) { cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"keyword"))) { printf("keyword: %s\n", xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)); } cur = cur->next; } return; } Again we get the first child node. Like the loop above, we then iterate through the nodes, looking for one that matches the element we're interested in, in this case "keyword". When we find the "keyword" element, we need to print its contents. Remember that in XML, the text contained within an element is a child node of that element, so we turn to cur->xmlChildrenNode. To retrieve it, we use the function xmlNodeListGetString, which also takes the doc pointer as an argument. In this case, we just print it out. Writing element content Writing element content uses many of the same steps we used above — parsing the document and walking the tree. We parse the document, then traverse the tree to find the place we want to insert our element. For this example, we want to again find the "storyinfo" element and this time insert a keyword. Then we'll write the file to disk. Full code: The main difference in this example is in parseStory: void parseStory (xmlDocPtr doc, xmlNodePtr cur, char *keyword) { xmlNewTextChild (cur, NULL, "keyword", keyword); return; } The xmlNewTextChild function adds a new child element at the current node pointer's location in the tree, specificied by cur. Once the node has been added, we would like to write the document to file. Is you want the element to have a namespace, you can add it here as well. In our case, the namespace is NULL. xmlSaveFormatFile (docname, doc, 1); The first parameter is the name of the file to be written. You'll notice it is the same as the file we just read. In this case, we just write over the old file. The second parameter is a pointer to the xmlDoc structure. Setting the third parameter equal to one ensures indenting on output. Writing Attribute Writing an attribute is similar to writing text to a new element. In this case, we'll add a reference URI to our document. Full code:. A reference is a child of the story element, so finding the place to put our new element and attribute is simple. As soon as we do the error-checking test in our parseDoc, we are in the right spot to add our element. But before we do that, we need to make a declaration using a datatype we have not seen yet: xmlAttrPtr newattr; We also need an extra xmlNodePtr: xmlNodePtr newnode; The rest of parseDoc is the same as before until we check to see if our root element is story. If it is, then we know we are at the right spot to add our element: newnode = xmlNewTextChild (cur, NULL, "reference", NULL); newattr = xmlNewProp (newnode, "uri", uri); First we add a new node at the location of the current node pointer, cur. using the xmlNewTextChild function. Once the node is added, the file is written to disk just as in the previous example in which we added an element with text content. Retrieving Attributes Retrieving the value of an attribute is similar to the previous example in which we retrieved a node's text contents. In this case we'll extract the value of the URI we added in the previous section. Full code: . The initial steps for this example are similar to the previous ones: parse the doc, find the element you are interested in, then enter a function to carry out the specific task required. In this case, we call getReference: void getReference (xmlDocPtr doc, xmlNodePtr cur) { cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"reference"))) { printf("uri: %s\n", xmlGetProp(cur, "uri")); } cur = cur->next; } return; } The key function is xmlGetProp, which returns an xmlChar containing the attribute's value. In this case, we just print it out. If you are using a DTD that declares a fixed or default value for the attribute, this function will retrieve it. Sample Document &STORY; Code for Keyword Example &KEYWORD; Code for Add Keyword Example &ADDKEYWORD; Code for Add Attribute Example &ADDATTRIBUTE; Code for Retrieving Attribute Value Example &GETATTRIBUTE;