Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

XPath Tutorial

XmlPath is Tango's answer to the very many XPath solutions provided by most languages. Although XmlPath can query the same data XPath can, you will find the interface to feel different, yet very refreshing. And it's more powerful to boot.

This article will reference this XML file and will require the file to be in the same directory as your D source file. Also, this article assumes the filename of the XML content is xpath.xml. If you use a different name you will have to modify the examples.

Now, first thing is first. I will walk you through loading an XML document using the Tango File shortcut.

Loading an XML file

We are going to use the File device to load the contents of xpath.xml into a xml buffer and then print the length of the buffer. We will then create a new Document instance and tell it to parse the XML content from the xml buffer.

import tango.io.device.File;
import tango.io.Stdout;
import tango.text.xml.Document;

void main () {
    // load our xml document
    auto xml  = cast(char[])File.get("xpath.xml");

    // create document
    auto doc = new Document!(char);
    doc.parse(xml);

    // get the root element
    auto root = doc.elements;

    // query the doc for all country elements
    auto result = root.query.descendant("country");

    foreach (e; result) {
        Stdout(e.value).newline;
    }
}

Our first query

Let's iterate all <country> elements and print their values out.

import tango.io.device.File;
import tango.io.Stdout;
import tango.text.xml.Document;

void main () {
    // load our xml document
    auto xml  = cast(char[])File.get("xpath.xml");

    // create document
    auto doc = new Document!(char);
    doc.parse(xml);

    // get the root element
    auto root = doc.elements;

    // query the doc for all country elements
    auto result = root.query.descendant("country");

    foreach (e; result) {
        Stdout(e.value).newline;
    }
}

Querying attributes

XmlPath also provides a quick method for querying attributes. We simply append attribute along with the attribute name. The significant difference with this query is that the Node instance returned is the attribute Node and not the element one. Both have the same value property that can be accessed. You will notice that it also prints the id attribute value and not the element value, and this is because we appended an attribute specific query onto the element query.

import tango.io.device.File;
import tango.io.Stdout;
import tango.text.xml.Document;

void main () {
    // load our xml document
    auto xml  = cast(char[])File.get("xpath.xml");

    // create document
    auto doc = new Document!(char);
    doc.parse(xml);

    // get the root element
    auto root = doc.elements;

    // query the doc for all country elements with an id attribute (all of them)
    auto result = root.query.descendant("country").attribute("id");

    foreach (e; result) {
        Stdout(e.value).newline;
    }
}

Advanced querying using filters

You thought XPath was nice, but you haven't seen anything yet. XmlPath lets you filter using delegates. Take that, XPath! For this example we're going to query for all countries and then we're going to filter them using a delegate that checks for a letter 'B' in the id attribute.

import tango.io.device.File;
import tango.io.Stdout;
import Util = tango.text.Util;
import tango.text.xml.Document;

void main () {
    // load our xml document
    auto xml  = cast(char[])File.get("xpath.xml");

    // create document
    auto doc = new Document!(char);
    doc.parse(xml);

    // a custom filter
    bool test(doc.Node node)
    {
        auto attr = node.attributes.name(null, "id");
        return attr && Util.contains(attr.value, 'B');
    }

    // get the root element
    auto root = doc.elements;

    // query the doc for all country elements with an id attribute that contains a 'B'
    auto result = root.query.descendant("country").filter(&test);

    foreach (e; result) {
        Stdout.format("id = {}, name = {}", e.attributes.name(null, "id").value, e.value).newline;
    }
}