NSXMLDocument, xpath and namespaces

I’ve had this problem before with other APIs, and found very little info when googling. The problem is thus: suppose I have some XML like the following:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://namespaces.com/myFirstNameSpace">
    <chapter xmlns="http://namespaces.com/myFirstNameSpace">
        <page xmlns="http://pagenamespace.com/ns">My Page</page>
    </chapter>
</book>

As you can see, there are two namespaces used. One namespace is used for book and chapter, and another is used for page.

Neither of those namespaces are given a prefix. If page had been given the prefix pns, you could use the prefix when performing xpath queries:

/book/chapter/pns:page

However, you can’t use the full URL of a namespace in an xpath query. You could do something complicated like this:

/book/chapter/*[namespace-uri()='http://pagenamespace.com/ns' and local-name()='page']

…but that’s ugly.

So, assuming you can’t change the source XML, what can you do to be able to query this? If you were using libxml, you could do this:

 xmlXPathRegisterNs(myxmldoc, "pns", "http://pagenamespace.com/ns");

but I couldn’t find the equivalent on NSXMLDocument. 

The solution that I found was to add additional namespace declarations to the root node of the document, which match the URL of the namespace in question and have a prefix. Thus:

	NSXMLElement *namespace = [NSXMLElement namespaceWithName:@"pns" stringValue:@"http://pagenamespace.com/ns"];
	[xmldoc.rootElement addNamespace:namespace];

After you’ve done that, you can query using the prefix you just defined:

NSArray *pages = [xmldoc.rootElement nodesForXPath:@"/book/chapter/pns:page" error:&error];
Previous
Previous

Everything needs cloud sync, but not via iCloud

Next
Next

On 2.0 and rethinking an app