Home > Net >  C libxml2 : Parse xmlPath to traverse in a loop and extract parameters of grandchild nodes. Possible
C libxml2 : Parse xmlPath to traverse in a loop and extract parameters of grandchild nodes. Possible

Time:03-03

I am a beginner in using Xpath and XML. I have libxml2 installed and I can parse the XML file successfully. I can also read value of a particular node by using xmlXPathContext. The problem statement and example.xml is as below.

I have implemented the same functionality in shell by running a for loop for all children of Root that is, Child 1,2, and 3 to find the match. Then I store the index# of Child where match is found. For this particular child, extract values for Param1 and Param2 for the GChild. Can we run similar loop using xpath? How do we reference the Child in a loop? "Root/Child[i]/" ?

For example, I get input as "ABCD2". Then I need to search for "ABCD2" by traversing through all Child 1,2,3. When we know it is Child 2 where the match lies, extract the Param1 and Param2 of that particular GChild of Child i.e GChild2 of Child2. That is, we should get "HIJK123"" and "HIJK345" as output in 2 variables.

example.xml
<Root>
    <Child> 
        <GChild>    
        <Param> "ABCD1" </Param>
        </GChild>
        
        <GChild>    
            <Param> "EFGH123" </Param>                          
            <Param> "EFGH345" </Param>
        </GChild>

        <GChild>    
        <Param> "IJKL123" </Param>
        </GChild>
    </Child>
        
    <Child>
        <GChild>    
        <Param> "ABCD2" </Param>
        </GChild>
        
        <GChild>
            <Param> "HIJK123" </Param>                          
            <Param> "HIJK345" </Param>
        </GChild>

        <GChild>    
        <Param> "LMNO123" </Param>
        </GChild>
    </Child>

    <Child>
        <GChild>    
        <Param1> "ABCD3" </Param1>
        </GChild1>
        
        <GChild>    
            <Param> "PQRS123" </Param>                          
            <Param> "PQRS345" </Param>
        </GChild>

        <GChild>    
        <Param> "TUVW123" </Param>
        </GChild>
    </Child>
</Root>

CodePudding user response:

If all you want to do is to return all the Param nodes from the second GChild element of any Child that contains an input-matching Param node in the first GChild element, then this XPath will work:

//*/GChild[2]/Param[../../GChild[1]/Param[contains(text(),'ABCD2')]]

You can try the XPath at http://xpather.com/DCioM5P4

Here is some sample code using that XPath and your sample XML that works:

#include <libxml/xpath.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    if(argc<2) return printf("Usage: %s <search_value>\n",argv[0]);

    char xPath[100];
    sprintf(xPath,"//*/GChild[2]/Param[../../GChild[1]/Param[contains(text(),'%s')]]",argv[1]);

    char buffer[] = "<?xml version=\"1.0\"?>\n\
        <Root>\n\
            <Child>\n\
                <GChild>\n\
                    <Param> \"ABCD1\" </Param>\n\
                </GChild>\n\
                <GChild>\n\
                    <Param> \"EFGH123\" </Param>\n\
                    <Param> \"EFGH345\" </Param>\n\
                </GChild>\n\
                <GChild>\n\
                    <Param> \"IJKL123\" </Param>\n\
                </GChild>\n\
            </Child>\n\
            <Child>\n\
                <GChild>\n\
                    <Param> \"ABCD2\" </Param>\n\
                </GChild>\n\
                <GChild>\n\
                    <Param> \"HIJK123\" </Param>\n\
                    <Param> \"HIJK345\" </Param>\n\
                </GChild>\n\
                <GChild>\n\
                    <Param> \"LMNO123\" </Param>\n\
                </GChild>\n\
            </Child>\n\
            <Child>\n\
                <GChild>\n\
                    <Param> \"ABCD3\" </Param>\n\
                </GChild>\n\
                <GChild>\n\
                    <Param> \"PQRS123\" </Param>\n\
                    <Param> \"PQRS345\" </Param>\n\
                </GChild>\n\
                <GChild>\n\
                    <Param> \"TUVW123\" </Param>\n\
                </GChild>\n\
            </Child>\n\
        </Root>\n\
    ";
    xmlDocPtr document = xmlParseDoc((xmlChar*)buffer);

    xmlXPathContextPtr xPathCtx = xmlXPathNewContext(document);
    xPathCtx->node = xmlDocGetRootElement(document);

    xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((xmlChar*)xPath, xPathCtx);
    if(xpathObj == NULL){
        printf("Failed to evaluate xpath\n");
    }

    xmlNodeSetPtr nodeset = xpathObj->nodesetval;
    if(!xmlXPathNodeSetIsEmpty(nodeset)){
        printf("Found nodes using: %s\n",xPath);
        for(int i=0; i<xmlXPathNodeSetGetLength(nodeset); i  ){
            xmlNodePtr node = xmlXPathNodeSetItem(nodeset,i);
            printf("<%s>%s</%s>\n",node->name,xmlNodeGetContent(node),node->name);
        }
    }else{
        printf("Failed to find nodes using: %s\n",xPath);
    }

    xmlXPathFreeObject(xpathObj);
    xmlXPathFreeContext(xPathCtx);

    return 0;
}
  • Related