KissXML
                                
                                 KissXML copied to clipboard
                                
                                    KissXML copied to clipboard
                            
                            
                            
                        nodesForXPath fails if xml document has DOCTYPE
If you add a DOCTYPE to the xml in testNodesForXPath you'll see that test 6 fails. For instance I added the following.
[xmlStr appendString:@"<?xml version=\"1.0\"?>"];
[xmlStr appendString:@"<!DOCTYPE AirSync PUBLIC \"-//AIRSYNC//DTD AirSync//EN\" \"http://www.microsoft.com/\">"];
[xmlStr appendString:@"<menu xmlns:a=\"tap\">"];
It fails because in DDXMLNode nodesForXPath:error: because the line
xmlNodePtr rootNode = (doc)->children;
returns the a node with the name AirSync instead of the real root node. I changed this to
    xmlNodePtr rootNode = xmlDocGetRootElement(doc);
Also if the xml uses a default namespace such as
<Sync xmlns="http://synce.org/formats/airsync_wm5/airsync">
and you try to add a prefixed namespace to the root node such as
<Sync xmlns=as="http://synce.org/formats/airsync_wm5/airsync">
it will fail. This is because a non-prefixed namespace is added in the line
    xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href);
I changed this to
            if (ns->prefix && ns->prefix[0] != '\0') {
                xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href);
            }
and now it works. In summary I changed
    xpathCtx = xmlXPathNewContext(doc);
    xpathCtx->node = (xmlNodePtr)genericPtr;
    xmlNodePtr rootNode = (doc)->children;
    if(rootNode != NULL)
    {
        xmlNsPtr ns = rootNode->nsDef;
        while(ns != NULL)
        {
            xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href);
            ns = ns->next;
        }
    }
to
    xpathCtx = xmlXPathNewContext(doc);
    xpathCtx->node = (xmlNodePtr)genericPtr;
    xmlNodePtr rootNode = xmlDocGetRootElement(doc);
    if(rootNode != NULL)
    {
        xmlNsPtr ns = rootNode->nsDef;
        while(ns != NULL)
        {
            if (ns->prefix && ns->prefix[0] != '\0') {
                xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href);
            }
            ns = ns->next;
        }
    }
Thanks for the fixes, I was getting mad at this. However, there is still a major problem with default namespaces. In other words, if you add a default namespace on the root node, as in:
[xmlStr appendString:@"<menu xmlns=\"restaurant\" xmlns:a=\"tap\">"];
then test 4 fails, both with your fix and without. Presumably, all other tests after test 4 should also fail. This is really annoying. According to this post http://www.perlmonks.org/?node_id=530519 the behaviour of KissXML seems to actually be the correct one, while NSXML is wrong. The problem boils down to the fact that, according to post above, "/menu/pizza" shall match both "menu" and "pizza" in the null namespace, not in the default one.
However, since:
- 
one of the design goals of KissXML is to be a replacemente with NSXML 
- 
the current behaviour makes XPath useless whenever you have a default namespace (there's no way to match an element in the default namespace, since the XPath syntax doesn't allow you to specify a namespace that doesn't have a prefix) 
I hope this could be fixed somehow.
Waiting for a better fix, I am now using this one instead of yours:
xmlNodePtr rootNode = xmlDocGetRootElement(doc);
if(rootNode != NULL)
{
    xmlNsPtr ns = rootNode->nsDef;
    while(ns != NULL)
    {
        const xmlChar* prefix = ns->prefix;
        if (!prefix || !*prefix)
        {
            prefix = rootNode->name;
        }
        xmlXPathRegisterNs(xpathCtx, prefix, ns->href);
        ns = ns->next;
    }
}
with this, at least there's a way to match elements in the default namespace (for example you can write "/restaurant:menu/restaurant:pizza" instead of "/menu/pizza"). Ugly, but better than nothing.