4

I am trying to write a function that will find a node with a specified name in a xml file. The problem is that the function never finds the specified node.

xmlNodePtr findNodeByName(xmlNodePtr rootnode, const xmlChar * nodename)
{
    xmlNodePtr node = rootnode;
    if(node == NULL){
        log_err("Document is empty!");
        return NULL;
    }

    while(node != NULL){

        if(!xmlStrcmp(node->name, nodename)){
            return node; 
        }
        else if(node->children != NULL){
            node = node->children; 
            xmlNodePtr intNode =  findNodeByName(node, nodename); 
            if(intNode != NULL){
                return intNode;
            }
        }
        node = node->next;
    }
    return NULL;
}

I can see in the debugger that function does go deep into the sub nodes but still returns NULL.

Thanks in advance.

2
  • 1
    Is there a reason you aren't using the XPath API? Commented Oct 23, 2012 at 4:17
  • No, there is no reason:) I just started using libxml2 so I'm only playing around with it. Commented Oct 23, 2012 at 7:28

3 Answers 3

8
else if(node->children != NULL) {
    node = node->children; 
    xmlNodePtr intNode =  findNodeByName(node, nodename); 
    if (intNode != NULL) {
        return intNode;
    }
}

This should be:

else if (node->children != NULL) {
    xmlNodePtr intNode =  findNodeByName(node->children, nodename); 
    if(intNode != NULL) {
        return intNode;
    }
}

and it works fine

Sign up to request clarification or add additional context in comments.

Comments

4

Your function is correct. Add a debugging line to see why it doesn't work in your case. For example:

    printf("xmlStrcmp(%s, %s)==%d\n", node->name, nodename,
        xmlStrcmp(node->name, nodename));

But you don't really need that function. You may use xmlXPathEval.

2 Comments

Ok, I'm gonna try the XPath API then...as soon as I get my function working;). Thanks
Function is not correct. I've given specified the correction in the next answer. Please check.
-1

When @jarekczek mentionned XPath, he means to use XPath instead of your function.

With XPath, your function will become:

xmlNodeSetPtr findNodesByName(xmlDocPtr doc, xmlNodePtr rootnode, const xmlChar* nodename) {
    // Create xpath evaluation context
    xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
    if(xpathCtx == NULL) {
        fprintf(stderr,"Error: unable to create new XPath context\n");
        return NULL;
    }

    // The prefix './/' means the nodes will be selected from the current node
    const xmlChar* xpathExpr = xmlStrncatNew(".//", nodename, -1);
    xmlXPathObjectPtr xpathObj = xmlXPathNodeEval(rootnode, xpathExpr, xpathCtx);
    if(xpathObj == NULL) {
        fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
        xmlXPathFreeContext(xpathCtx);
        return NULL;
    }

    xmlXPathFreeContext(xpathCtx);

    return xpathObj->nodesetval;
}

Note: This function returns all the nodes matching 'nodename'. If you only want the first node, you could replace return xpathObj->nodesetval; by:

if (xpathObj->nodesetval->nodeNr > 0) {
    return xpathObj->nodesetval->nodeTab[0];
} else {
    return NULL;
}

You could also append [1] to the XPath query to optimize your query.

2 Comments

shouldn't the code snippet also have xmlFree(xpathExpr) to avoid a memory leak from xmlStrncatNew?
Also xpathObj should be freed using xmlXPathFreeObject(xpathObj)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.