Traversing an XML Tree

NSXML gives you several ways to explore the tree structure representing an XML document and find nodes that are of interest. Each approach is based on a different conceptual model:

Traversing a tree in document order is perhaps the simplest approach. You start at some node in the tree—specifically the root element if you want to go through the entire document—and iteratively invoke nextNode to get the next node in document order until you reach the end of the document. Along the way you can query nodes for attributes of interest, such as name, value, kind, or level. Listing 1 illustrates the use of nextNode, in this case extracting comments that are used as translation notes for the string value of the subsequent node.

Listing 1  Walking an XML tree with nextNode messages

NSXMLNode *aNode = [xmlDoc rootElement];
NSMutableString *translator_notes=nil;
while (aNode = [aNode nextNode]) {
    if ( [aNode kind] == NSXMLCommentKind ) {
        if (!translator_notes) {
            translator_notes = [[NSMutableString alloc] init];
        }
        [translator_notes appendString:[aNode stringValue]];
        [translator_notes appendString:@" ========> "];
        aNode = [aNode nextNode]; // element to be translated
        [translator_notes appendString:[aNode stringValue]];
        [translator_notes appendString:@"\n"];
    }
}
if (translator_notes) {
    [translator_notes writeToFile:[NSString stringWithFormat:@"%@/translator_notes.txt", NSHomeDirectory()] atomically:YES];
    [translator_notes release];
}

Of course you can also go backward in document order by repeatedly sending previousNode to each returned node object.

While nextNode and previousNode take you sequentially through a represented XML document, many other NSXMLNode methods help you navigate hierarchically within an XML tree structure, between parent and children nodes and among the sibling nodes of a common parent. These methods include children , childCount , childAtIndex: , nextSibling , and previousSibling . The following code-fragment examples show how these methods might be used in combination to traverse sibling nodes and accomplish some task. In Listing 2, the children and childCount methods are used, along with the NSArray objectAtIndex: method.

Listing 2  Using the children and childCount methods to traverse sibling nodes

NSArray *children = [[xmlDoc rootElement] children];
int i, count = [children count];
for (i=0; i < count; i++) {
    NSXMLNode *child = [children objectAtIndex:i];
    [self doSomethingWithNode:child];
}

If you can, use the NSXMLNode childCount method to obtain the number of child nodes instead of sending count to the result of children. The former method offers better performance. The code example in Listing 3 is slightly simpler and bypasses the children method altogether.

Listing 3  Using the childAtIndex: and childCount methods to traverse sibling nodes

int i, count = [[xmlDoc rootElement] childCount];
for (i=0; i < count; i++) {
    NSXMLNode *child = [[xmlDoc rootElement] childAtIndex:i];
    [self doSomethingWithNode:child];
}

An even simpler approach to the same task is illustrated in Listing 4, which uses the NSXMLNode childAtIndex: and nextSibling methods.

Listing 4  Using the childAtIndex: and nextSibling methods to traverse sibling nodes

NSXMLNode *child = [[xmlDoc rootElement] childAtIndex:0];
do {
    [self doSomethingWithNode:child];
} while ( child = [child nextSibling] );

For going upward in the tree hierarchy, from child node to parent, you have the parent method. This is the only method needed for this direction because, except for the root element and standalone nodes, there is almost always a one-to-one relationship from a child to its parent in an XML tree. (Namespace and attribute nodes are also an exception to this relationship rule because they have an element as a parent but are not children of that element.)

If you want a more directed search, you can use the elementsForName: method. If you know the name of a child element, send elementsForName: to the parent element. This method returns the child NSXMLElement nodes with matching names in an array (an NSArray object is used in case more than one child has the specified name). If you have to deal with namespace-qualified elements, use the elementsForLocalName:URI: method instead.