views:

885

answers:

2

Hi,

To sum up, i'm a totally beginner in libxml and I have to use an existing source code. The main idea is to apply a first xpath expression to extract a set of nodes from an xml file. Then, for each node, the second xpath expression shall be applied to extract some values.

Existing source code is:

int xt_parseXmlResult(xmlDocPtr doc, const char *xpath, assoc_arrayc_t expr, arrayc_t *result) { xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);

// Register namespaces ...

/*
 * Evaluate main xpath expression
 */
xmlXPathObjectPtr xpathNodes = xmlXPathEvalExpression((xmlChar *)xpath, xpathCtx);

/*
 * Now we apply the xpath expressions on each node returned by the first xpath request
 */
// First loop is on the XML document as we have to create a new context each
// time we change the document
int nbDocs = xpathNodes->nodesetval->nodeNr;

for (row = 0; row < nbDocs; row++)
{
    xmlXPathContextPtr  subCtx = xmlXPathNewContext(doc);

 // Register namespaces ...

    // Update context to use the nodeset related to this row
    subCtx->node = xpathNodes->nodesetval->nodeTab[row];

    for (col = 0; col < expr.nbItems; col++)
    {
        // Evaluate expression
        xpathRows = xmlXPathEvalExpression((xmlChar *)expr.itemList[col].val, subCtx);

        result->data[(row + 1) * result->nbCols + col] = strdup((char *)xmlXPathCastToString(xpathRows)); 
        xmlXPathFreeObject(xpathRows);
    }
    xmlXPathFreeContext(subCtx);
    subCtx = NULL;
}

xmlFreeDoc(doc); 
xmlXPathFreeContext(xpathCtx);
xmlXPathFreeObject(xpathNodes);
return 0;

}

I think that the problem comes from this line

    // Update context to use the nodeset related to this row
subCtx->node = xpathNodes->nodesetval->nodeTab[row];

Because the second xpath expression is applied from the root of the xml file, not the root of each node.

Any idea on how to do such thing?

Best regards, Quentin

+1  A: 

you could concatinate your xpath expressions.

edit

//FORECAST/DAY/descendant::content/meteo/desc should work

Jeremy French
Thanks for your answer.Unfortunatly it is not so simple. For example the first xpath expresion can be "//FORECAST/DAY" and the second can be "//content/meteo/desc". So the concatination "//FORECAST/DAY//content/meteo/desc" is invalid.Quentin
Quentin
@Quentin //FORECAST/DAY//content/meteo/desc *is* a syntactically correct XPath expression!
Dimitre Novatchev
A: 

Some sample code. Modify to suit your needs and language. This is C#, but it should be largely the same. Notice the second xpath is not starting with a "/" and is using an instance of the node returned from the first one. Neither of the xpaths end in a "/".

XmlDocument doc = new XmlDocument();
doc.Load(docfile);
XmlNodeList items = doc.SelectNodes("/part1/part2");
foreach (item in items)
{
    XMLNode x = item.SelectNodes("part3");
    //Dostuff
}
Brian
Thanks for your help.I understand your sample code, but my problem is that with libxml I did not find how to apply an xpath expression to a node.When I apply my first xpath expresion on my xml context, I obtain a node set. And then , I do not know how to apply the expression from returned nodes...
Quentin
Finally, I change the xml library. I use now TouchXML and it works great (one of the TouchXML unit tests does exactly what I need).Regards,Quentin
Quentin
@Quentin //FORECAST/DAY//content/meteo/desc *is* a syntactically correct XPath expression! The approach of Jeremy French is more elegant and universal -- it works regardless of which XPath engine or programming language is used.
Dimitre Novatchev
I suspect my approach is faster if you are going to use a lot of different choices consecutively for the 2nd half of the xpath. At the very least, it's more in line with the intention. I only had one line and one subnode in my example but obviously in that case it's kind of silly.
Brian