Cinder provides the XmlTree class for processing XML. This class is recursive, meaning an XmlTree can contain other XmlTrees as its children. This design is similar to XML itself, which is a hierarchical format where an element can contain children elements.
For some of the example code in this document, we'll refer to this very basic XML document:
To parse a block of XML, construct an XmlTree using a DataSource. For example, to parse an XML file on your disk, use loadFile():
To parse XML on an http server, use loadUrl():
To parse XML contained in a resource (discussed in more depth here), use loadResource():
To parse XML contained in a string:
To grab a particular child of a node by name, use the XmlTree::getChild() method:
If the child does not exist, a XmlTree::ExcChildNotFound exception is thrown. To test for the existence of a child use XmlTree::hasChild():
XmlTree also supports finding children by path, where each component of the path is separated by the /
character. For example:
Output:
The example above shows a convenient way to examine an XML node, which is to pass it to a std::ostream like console(). Another convenient function is XmlTree::getPath(), which returns the path up to and including a given node:
Output:
To iterate the children of an XML node, use the XmlTree::Iter class.
Output:
You can also iterate the children of a node with the path syntax. For example, to iterate all the tracks of the music library, we can do something like the code below. Notice that the XmlTree::Iter is smart about finding all the nodes which match the path - there are tracks from both albums, not just the first.
Output:
By default paths for the XmlTree are case insensitive. An optional boolean following the path allows you to force case sensitivity. Also for the uncommon case in which your node tags contain the '/' character, you can supply an alternate separator - we use the '.' below:
As we've already seen, you can get the tag (or name) of a node by calling getTag(). To get the value of a node as a string use getValue(). As an additional convenience, you can have XmlTree parse a string for you for any type which supports the istream>> operator. For example, if you know the nodes' values are floats, you might do this:
The XmlTree also offers facility for walking a node's attributes. To get the value of an attribute as a string, you can call getAttributeValue():
There is also a variant which supports automatic type conversion:
As an additional convenience, you can supply a default value in the case that a node does not have a particular attribute. If we wanted our default size to be 1 for nodes which do not posses a size attribute, we would do this:
The XmlTree class can be used to build and write XML documents as well. The example below creates a music library with one album and prints it to the console():
Output:
Notice that the node echoed to the console was not treated as an XML document - it lacks the <?xml>
declaration of a true XML document. There are a couple of ways of achieving this. The simplest is to use XmlTree::write(), which by default assumes you want a full XML document:
This routine has an optional second boolean parameter which will create the <?xml>
declaration when true, its default value. Another option would be to create a document node ourselves and append the <library>
to that:
The XmlTree is designed to be const-correct, and supports a ConstIter which mimicks the const_iterator
of STL containers:
Output:
It's also worth noting the value of passing XmlTrees by reference whenever possible. XmlTrees create a full copy of the XML data tree whenever they are copied, so passing by reference can improve performance significantly. Furthermore, assigning by copy will prevent us from modifying the "original" node of an XmlTree should we so desire. For example:
Output:
Instead, use a reference in order to modify the XmlTree:
Output:
The XmlTree::Iter and XmlTree::ConstIter are designed to be STL-compatible iterators. For example, if you are using a lambdas-aware C++ compiler (currently only VC2010 at the time of this writing) the following code prints the names of the albums in the music library:
XmlTree is implemented using the RapidXML library. For unusually performance-conscious use cases, it is worth considering using RapidXML directly, as the XmlTree is designed to be convenient more than it is fast. The necessary header files are in cinder/include/rapidxml and can be #include
d like so: