# Libxml Tutorial

This tutorial is based on the Libxml Tutorial by John Fleck ( ©2002, 2003 ), converted for BlitzMax by Bruce Henderson

Table of Contents

- Introduction
- Data Types
- Parsing the file
- Retrieving Element Content
- Using XPath to Retrieve Element Content
- Writing Element content
- Writing Attribute
- Retrieving Attributes
- A. Sample Document
- B. Code for Keyword Example
- C. Code for XPath Example
- D. Code for Add Keyword Example
- E. Code for Add Attribute Example
- F. Code for Retrieving Attribute Value Example

## Introduction

Libxml is a BlitzMax library implementing functions for reading, creating and manipulating XML data. This tutorial provides example code and explanations of its basic functionality.

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, 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.

Table of Contents

## Data Types

Libxml declares a number of data types we will encounter repeatedly, hiding the messy stuff so you do not have to deal with it unless you have some specific need.

TxmlDoc
  A structure containing the tree created by a parsed doc.
TxmlNode
  A structure containing a single node.

Table of Contents

## Parsing the file

Parsing the file requires only the name of the file and a single function call, plus error checking. Full code: Appendix B, Code for Keyword Example

    	 Local doc:TxmlDoc
    	 Local node:TxmlNode

    	 doc = TxmlDoc.parseFile(docname)

    	If doc = Null Then
    		Print "Document not parsed successfully."
    		Return
    	End If

    	 node = doc.GetRootElement()

    	If node = Null Then
    		Print "empty document"
    		doc.free()
    		Return
    	End If

    	If node.getName() = "storyinfo" Then
    		parseStory(doc, node)
    	End If

Declare the variable that will refer to your parsed document.

Declare a node variable (you'll need this in order to interact with individual nodes).

Check to see that the document was successfully parsed. If it was not, libxml will at this point register an error and stop.

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 the documents used in this tutorial.

Table of Contents

## 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 TxmlDoc called `doc` and an TxmlNode called `node`.

    	Local nodeList:TList = node.getChildren()
    	Foreach node = EachIn nodeList
    			If node.getName() = "storyinfo" Then
    				parseStory(doc, cur)
    			End If
    	Next

Get list of the child nodes of `node`. At this point, `node` 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. Once found, it calls the function `parseStory`.

    Function parseStory(doc:TxmlDoc, node:TxmlNode)

    	Local key:String
    	Local children:TList = node.getChildren()

    	For node = EachIn children
    		If node.getName() = "keyword" Then
    			key = node.getText()
    			Print "keyword: " + key
    		End If
    	Next
    	Return
    End Function

Again we get thelist of child nodes.

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 `node.getText()` to retrieve it, which iterates through the child nodes returning all text elements. In this case, we just print it out.

Table of Contents

## Using XPath to Retrieve Element Content

Libxml includes support for use of XPath expressions to retrieve sets of nodes that match a specified criteria.

XPath allows searching through a document for nodes that match specified criteria. In the example below we search through a document for the contents of all `keyword` elements.

| Note
A full discussion of XPath is beyond the scope of this document. For details on its use, see the XPath specification.

Full code for this example is at Appendix C, Code for XPath Example.

Using XPath requires setting up an xmlXPathContext and then supplying the XPath expression and the context to the `xmlXPathEvalExpression` function. The function returns an xmlXPathObjectPtr, which includes the set of nodes satisfying the XPath expression.

    Function getnodeset:TxmlXPathObject(doc:TxmlDoc, xpath:String)

    	 Local context:TxmlXPathContext
     	Local result:TxmlXPathObject

    	 context = doc.newXPathContext()

    	 result = context.evalExpression(xpath)

    	 If result.nodeSetIsEmpty() Then
    		result.free()
     		Print "No result"
    		Return Null

First we declare our variables.

Initialize the `context` variable.

Apply the XPath expression.

Check the result and free the memory allocated to `result` if no result is found.

The TxmlXPathObject returned by the function contains a set of nodes and other information needed to iterate through the set and act on the results. For this example, our function returns the `TxmlXPathObject`. We use it to print the contents of `keyword` nodes in our document. The node set object includes the number of elements in the set (`getNodeCount()`) and an array of nodes (`getNodeList()`):

     For Local node:TxmlNode = EachIn nodeset.getNodeList()

    		 Local keyword:String = node.getText()
    		Print "keyword: " + keyword
    	Next

Here we use getNodeList() to iterate through the array of nodes.

Here we print the contents of each of the nodes returned.

| Note
Note that we are printing the child node of the node that is returned, because the contents of the `keyword` element are a child text node.

Table of Contents

## 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 contents to the console. Full code: Appendix D, Code for Add Keyword Example

The main difference in this example is in `parseStory`:

    Function parseStory (node:TxmlNode, keyword:String)

    	 node.addTextChild("keyword", Null, keyword)
    End Function

The `addTextChild` method adds a new child element at the current node's location in the tree, specified by `cur`.

Once the node has been added, we would like to write the document to the console. If you want the element to have a namespace, you can add it here as well. In our case, the namespace is Null.

    	doc.saveFormatFile("-", True)

The first parameter is the name of the file to be written. You'll notice it is "-". In this case, we are specifying we want to display the contents of the file on the console, rather than write it out to an actual physical file. Setting the second parameter to True ensures indenting on output.

Table of Contents

## 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: Appendix E, Code for Add Attribute Example.

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 data type we have not seen yet:

    	Local newattr:TxmlAttribute

We also need an extra TxmlNode:

    	Local newnode:TxmlNode

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 = node.addTextChild("reference", Null, Null)
    	 newattr = newnode.addAttribute("uri", uri)

First we add a new node at the location of the current node pointer, `node`, using the addTextChild function.

Next we add a new attribute to the new node, using the addAttribute function.

Once the node is added, the file is written to the console just as in the previous example in which we added an element with text content.

Table of Contents

## 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: Appendix F, Code for Retrieving Attribute Value Example.

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`:

    Function getReference(doc:TxmlDoc, node:TxmlNode)

    	Local uri:String

    	Local list:TList = node.getChildren()
    	For node = EachIn list
    		If node.getName() = "reference" Then
    		 uri = node.getAttribute("uri")
    			Print "uri: " + uri
    		End If
    	Next
    End Function

The key function is `addAttribute`, which returns an `String` containing the attribute's value. In this case, we just print it out.

| Note
If you are using a DTD that declares a fixed or default value for the attribute, this function will retrieve it.

Table of Contents

## A. Sample Document

    <?xml version="1.0"?>
    <story>
      <storyinfo>
        <author>John Fleck</author>

        <datewritten>June 2, 2002</datewritten>
        <keyword>example keyword</keyword>
      </storyinfo>
      <body>
        <headline>This is the headline</headline>

        <para>This is the body text.</para>
      </body>
    </story>

Table of Contents

## B. Code for Keyword Example

You can open the source here.

    SuperStrict

    Import BaH.Libxml

    Local docname:String = "sample.xml"
    parseDoc(docname)

    Function parseStory(doc:TxmlDoc, node:TxmlNode)

    	Local key:String
    	Local children:TList = node.getChildren()

    	For node = EachIn children
    		If node.getName() = "keyword" Then
    			key = node.getText()
    			Print "keyword: " + key
    		End If
    	Next
    	Return
    End Function

    Function parseDoc(docname:String)

    	Local doc:TxmlDoc
    	Local node:TxmlNode

    	doc = TxmlDoc.parseFile(docname)

    	If doc = Null Then
    		Print "Document not parsed successfully."
    		Return
    	End If

    	node = doc.GetRootElement()

    	If node = Null Then
    		Print "empty document"
    		doc.free()
    		Return
    	End If

    	If node.getName() <> "story"
    		Print "document of the wrong type, root node <> story"
    		doc.free()
    		Return
    	End If

    	Local children:TList = node.getChildren()

    	For node = EachIn children
    		If node.getName() = "storyinfo" Then
    			parseStory(doc, node)
    		End If
    	Next

    	doc.free()
    	Return
    End Function

Table of Contents

## C. Code for XPath Example

You can open the source here.

    SuperStrict

    Import BaH.Libxml

    Local docname:String = "sample.xml"
    Local doc:TxmlDoc

    Local xpath:String = "//keyword"
    Local nodeset:TxmlNodeSet
    Local result:TxmlXPathObject

    doc = getdoc(docname)
    If doc Then
    	result = getNodeSet(doc, xpath)
    	If result
    		nodeset = result.getNodeSet()

    		For Local node:TxmlNode = EachIn nodeset.getNodeList()

    			Local keyword:String = node.getText()
    			Print "keyword: " + keyword
    		Next
    		result.free()
    	End If

    	doc.free()
    End If

    xmlCleanupParser()
    End

    Function getdoc:TxmlDoc(docname:String)
    	Local doc:TxmlDoc = TxmlDoc.parseFile(docname)

    	If doc = Null Then
    		Print "Document not parsed successfully."
    		Return Null
    	End If

    	Return doc
    End Function

    Function getnodeset:TxmlXPathObject(doc:TxmlDoc, xpath:String)

    	Local context:TxmlXPathContext
     	Local result:TxmlXPathObject

    	context = doc.newXPathContext()
    	If context = Null Then
    		Print "Error in newXPathContext"
    		Return Null
    	End If
    	result = context.evalExpression(xpath)
    	context.free()

    	If result = Null Then
    		Print "Error in xmlXPathEvalExpression"
    		Return Null
    	End If

    	If result.nodeSetIsEmpty() Then
    		result.free()
     		Print "No result"
    		Return Null
    	End If
    	Return result
    End Function

Table of Contents

## D. Code for Add Keyword Example

You can open the source here.

    SuperStrict

    Import BaH.Libxml

    Local docname:String = "sample.xml"
    Local keyword:String = "Blitzmax"
    Local doc:TxmlDoc

    doc = parseDoc(docname, keyword)
    If doc <> Null Then
    	doc.saveFormatFile("-", False)
    	doc.free()
    End If

    Function parseStory(node:TxmlNode, keyword:String)
    	node.addTextChild("keyword", Null, keyword)
    End Function

    Function parseDoc:TxmlDoc(docname:String, keyword:String)

    	Local doc:TxmlDoc
    	Local node:TxmlNode

    	doc = TxmlDoc.parseFile(docname)

    	If doc = Null Then
    		Print "Document not parsed successfully."
    		Return Null
    	End If

    	node = doc.getRootElement()

    	If node = Null Then
    		Print "empty document"
    		doc.free()
    		Return Null
    	End If

    	If node.getName() <> "story" Then
    		Print "document of the wrong type, root node <> story"
    		doc.free()
    		Return Null
    	End If

    	Local nodelist:TList = node.getChildren()
    	For node = EachIn nodelist
    		If node.getName() = "storyinfo" Then
    			parseStory(node, keyword)
    		End If
    	Next

    	Return doc
    End Function

Table of Contents

## E. Code for Add Attribute Example

You can open the source here.

    SuperStrict

    Import BaH.Libxml

    Local docname:String = "sample.xml"
    Local uri:String = "http://blitzmax.com"
    Local doc:TxmlDoc

    doc = parseDoc(docname, uri)
    If doc <> Null Then
    	doc.saveFormatFile("-", True)
    	doc.free()
    End If

    Function parseDoc:TxmlDoc(docname:String, uri:String)

            Local doc:TxmlDoc
            Local node:TxmlNode
            Local newnode:TxmlNode
            Local newattr:TxmlAttribute

            doc = TxmlDoc.parseFile(docname)

            If doc = Null Then
                    Print "Document not parsed successfully."
                    Return Null
            End If

            node = doc.getRootElement()

            If node = Null Then
                    Print "empty document"
                    doc.free()
                    Return Null
            End If

            If node.getName() <> "story" Then
                    Print "document of the wrong type, root node <> story"
                    doc.free()
                    Return Null
            End If

            newnode = node.addTextChild("reference", Null, Null)
            newattr = newnode.addAttribute("uri", uri)
            Return doc
    End Function

Table of Contents

## F. Code for Retrieving Attribute Value Example

You can open the source here.

    SuperStrict

    Import BaH.Libxml

    Local docname:String = "sampleuri.xml"
    parseDoc(docname)

    Function getReference(doc:TxmlDoc, node:TxmlNode)

    	Local uri:String

    	Local list:TList = node.getChildren()
    	For node = EachIn list
    		If node.getName() = "reference" Then
    			uri = node.getAttribute("uri")
    			Print "uri: " + uri
    		End If
    	Next
    End Function

    Function parseDoc(docname:String)

            Local doc:TxmlDoc
            Local node:TxmlNode

            doc = TxmlDoc.parseFile(docname)

            If doc = Null Then
                    Print "Document not parsed successfully."
                    Return
            End If

            node = doc.getRootElement()

            If node = Null Then
                    Print "empty document"
                    doc.free()
                    Return
            End If

            If node.getName() <> "story" Then
                    Print "document of the wrong type, root node <> story"
                    doc.free()
                    Return
            End If

            getReference(doc, node)
            doc.free()
    End Function

Table of Contents