xmlxpathlibxml2vala

Node selection using XPath in libxml2


I have a XML document similar to the following.

<library>
  <artist name="a">
   <album name="1"></album>
   <album name="2"></album>
  </artist>
  <artist name="b">
   <album name="8"></album>
   <album name="9"></album>
  </artist>
</library>`

I am coding an application in Vala on Ubuntu and using the libxml2 library for manipulating the XML document. I wanted to fetch all the album nodes irrespective of their position in the document. I have written the following piece of code to do the aforementioned job.

document = Xml.Parser.parse_file (PATH_TO_DB);
...
Xml.XPath.Context context;
unowned Xml.XPath.Object *object;

context = new Xml.XPath.Context(document);
object = context.eval("//album");

if(object->nodesetval->is_empty())
    return null;

return object->nodesetval->item(0);
...

This XPath query only returns the albums under artist a, ie, albums 1 and 2. However, context.eval("/library/artist[@name="b"]/album"); is returning all the albums under artist b.

The python equivalent of the above shown code is producing the desired result.

doc = libxml2.parseFile("data.xml")
ctxt = doc.xpathNewContext()
res = ctxt.xpathEval("//album")

What is wrong with the Vala code ?


Solution

  • First, you should to check that the returned object is a NodeSet by examining object->type == Xml.ObjectType.NODESET.

    Next, you are only returning the first item in the resulting nodeset. There are nodeset->length() items.

    void main() {
        var document = Xml.Parser.parse_file ("file.xml");
        run_query("//album", document);
        run_query("/library/artist[@name=\"b\"]/album", document);
    }
    
    void run_query(string query, Xml.Doc* document) {
        stdout.printf("Query: %s\n", query);
        Xml.XPath.Context context;
        unowned Xml.XPath.Object *object;
    
        context = new Xml.XPath.Context(document);
        object = context.eval(query);
        assert(object->type == Xml.XPath.ObjectType.NODESET);
    
        if(object->nodesetval->is_empty()) {
            stdout.printf("empty");
            return;
        }
    
        for (var i = 0; i < object->nodesetval->length(); i++) {
            var node = object->nodesetval->item(i);
            stdout.printf("node %s name=\"%s\"\n", node->name, node->get_prop("name"));
        }
    }
    

    Produces:

    Query: //album
    node album name="1"
    node album name="2"
    node album name="8"
    node album name="9"
    Query: /library/artist[@name="b"]/album
    node album name="8"
    node album name="9"