DOM guide: Importing documents

From COLLADA Public Wiki
Revision as of 17:24, 10 April 2008 by SteveT (talk | contribs)
Jump to navigation Jump to search

Be sure to read the section on creating documents first. It covers some important topics relevant to this section.

A simple example

Let's begin with a simple example of reading some information from a Collada document. We'll open the document and print the ID of the first <node> we find.

DAE dae;
daeElement* root = dae.open("simpleImport.dae");
if (!root) {
    cout << "Document import failed.\n";
    return 0;
}

We create the DAE object then call DAE::open to open a file called "simpleImport.dae". If there is no file of that name in the current directory, or the file failed to open for some other reason, then the DAE::open method will return null. We check for that and print an error message if opening the document failed.

daeElement* node = root->getDescendant("node");
if (!node)
    cout << "No nodes found\n";
else
    cout << "node id: " << node->getAttribute("id") << endl;

Here we use the daeElement::getDescendant method to do a breadth-first search through the xml element tree for an element with the given name. This method will return null if it couldn't find an element with a matching name, which we check for. If it did find a matching element we use the daeElement::getAttribute method to print the value of the 'id' attribute.

The complete code.

#include <iostream>
#include <dae.h>
#include <dom/domCOLLADA.h>
using namespace std;

int main() {
	DAE dae;
	daeElement* root = dae.open("simpleImport.dae");
	if (!root) {
		cout << "Document import failed.\n";
		return 0;
	}

	daeElement* node = root->getDescendant("node");
	if (!node)
		cout << "No nodes found\n";
	else
		cout << "node id: " << node->getAttribute("id") << endl;

	return 0;
}

The simpleImport.dae document.

<?xml version="1.0" encoding="UTF-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
	<asset>
		<contributor/>
		<created>2008-04-08T13:07:52-08:00</created>
		<modified>2008-04-08T13:07:52-08:00</modified>
	</asset>
  <library_nodes>
    <node id="hello"/>
  </library_nodes>
</COLLADA>

And the results of running the program.

node id: hello

Reading data from elements

Any individual xml element has four types of data you might need: the element name, the element's attributes, the element's character data, and the element's child elements. The DOM provides easy access to all of this data via the daeElement interface.

Element name

Use the daeElement::getElementName method to get an element's name.

daeString getElementName() const; // Function signature
cout << elt->getElementName() << endl; // Example: print an element's name

Element attributes

To get the value of an attribute given the attribute's name, use the daeElement::getAttribute method.

std::string getAttribute(daeString name);

We've already seen an example of daeElement::getAttribute usage in the simple import example.

cout << "node id: " << node->getAttribute("id") << endl;

If you don't know what attributes an element has, you can iterate over its attribute list using the following methods of daeElement.

size_t getAttributeCount();
std::string getAttributeName(size_t i);
std::string getAttribute(size_t i);

This code snippet prints all the attribute names and values of the root element.

for (size_t i = 0; i < root->getAttributeCount(); i++) {
	cout << "attr " << i << " name: " << root->getAttributeName(i) << endl;
	cout << "attr " << i << " value: " << root->getAttribute(i) << endl;
}

Character data

You can retrieve an element's character data with the daeElement::getCharData method.

std::string getCharData();

For example, let's say you have an <asset> element and you want to tell if the <up_axis> setting is Z_UP. You could do that as follows.

daeElement* upAxis = asset->getDescendant("up_axis");
if (upAxis && upAxis->getCharData() == "Z_UP")
    // We have a match!

Child elements

If you know the name of the child element you want, you can access it with daeElement::getChild.

daeElement* getChild(daeString eltName);

This will return null if the element with the given name doesn't exist. You might use this function to test for the existence of a particular child element.

if (root->getChild("asset") == NULL)
    cout << "Missing <asset> element!\n"

If you don't have a specific element in mind you can get a list of all the child elements instead with the daeElement::getChildren method.

daeTArray< daeSmartRef<daeElement> > getChildren();

It returns an array of smart pointers to daeElement objects, which you can simply treat like ordinary daeElement pointers. You can use daeElement::getChildren to print a list of all the child elements of root like this.

daeTArray<daeElementRef> children = root->getChildren();
for (size_t i = 0; i < children.getCount(); i++)
	cout << "child " << i << " name: " << children[i]->getElementName() << endl;

daeElementRef is just a typedef for daeSmartRef<daeElement> that's made available to DOM clients to keep code simpler.

The dom* classes

As was mentioned in the creating documents section, the dom* classes provide an alternative interface to working with elements in the DOM. All of the operations discussed so far can be done with the dom* classes instead of the daeElement interface. For example, the code to print the id attribute of the first <node> in the document could've been written like this instead:

domNode* node = (domNode*)root->getDescendant("node");
if (!node)
	cout << "No nodes found\n";
else
	cout << "node id: " << node->getId() << endl;

The dom* classes provide a more strongly typed interface to the Collada elements, and sometimes this can be convenient. Use your judgment to decide between the daeElement interface and a dom* class for a given task.

Element hierarchy traversal

An xml document contains a tree of elements. Each element has a list of children, and each child has its own list of children, and so on. The DOM provides several methods in the daeElement interface for easily navigating a document's element tree.

Searching downward

You can search downward through the element tree using the daeElement::getChild and daeElement::getDescendant methods.

daeElement* getChild(daeString eltName);
daeElement* getDescendant(daeString eltName);

We've already seen these methods used in previous examples. getDescendant does a breadth-first search down the element tree, looking for a node with the given name. getChild works exactly the same, except that it only goes one level deep.

Searching upward

To search upward, use the daeElement::getParent and daeElement::getAncestor functions.

daeElement* getParent();
daeElement* getAncestor(daeString eltName);

getParent doesn't do a "search" exactly. Since an element only has one parent, getParent simply returns that element. getAncestor goes all the way up the element tree to the root searching for an element with the given name.

All the methods for element hierarchy traversal return null if a matching element isn't found.