Transforming XML with Mustache Templates

Although XSLT is a powerful and flexible way to convert XML documents to alternate representations, it is often difficult and inconvenient to use in practice. The XSLT specification is large and complex, and platform support is inconsistent.

As of HTTP-RPC 7.4, the TemplateEncoder class can be used to transform XML using a lightweight syntax similar to Mustache. The new ElementAdapter class provides access to the contents of an XML DOM Element via the Map interface. The contents of this map can be easily transformed to another representation via a template document.

For example, this tutorial from W3 Schools uses XML to model a simple breakfast menu:

<?xml version="1.0" encoding="UTF-8"?>

<breakfast_menu>
    <food>
        <name>Belgian Waffles</name>
        <price>$5.95</price>
        <description>Two of our famous Belgian Waffles with plenty of real maple syrup</description>
        <calories>650</calories>
    </food>

    <food>
        <name>Strawberry Belgian Waffles</name>
        <price>$7.95</price>
        <description>Light Belgian waffles covered with strawberries and whipped cream</description>
        <calories>900</calories>
    </food>

    <food>
        <name>Berry-Berry Belgian Waffles</name>
        <price>$8.95</price>
        <description>Light Belgian waffles covered with an assortment of fresh berries and whipped cream</description>
        <calories>900</calories>
    </food>

    <food>
        <name>French Toast</name>
        <price>$4.50</price>
        <description>Thick slices made from our homemade sourdough bread</description>
        <calories>600</calories>
    </food>

    <food>
        <name>Homestyle Breakfast</name>
        <price>$6.95</price>
        <description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
        <calories>950</calories>
    </food>
</breakfast_menu>

The example uses the following XSLT template to transform the markup to HTML. Even in this fairly simple example, the verbosity of XSLT is obvious, and has a negative impact on readability:

<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
        <xsl:for-each select="breakfast_menu/food">
            <div style="background-color:teal;color:white;padding:4px">
                <span style="font-weight:bold"><xsl:value-of select="name"/> - </span>
                <xsl:value-of select="price"/>
            </div>
            <div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
                <p>
                    <xsl:value-of select="description"/>
                    <span style="font-style:italic"> (<xsl:value-of select="calories"/> calories per serving)</span>
                </p>
            </div>
        </xsl:for-each>
    </body>
</html>

The javax.xml.transform.Transformer class can be used to apply the template to the XML document:

Source source = new StreamSource(getClass().getResourceAsStream("breakfast_menu.xslt"));

Transformer transformer = TransformerFactory.newInstance().newTransformer(source);

Source xmlSource = new StreamSource(getClass().getResourceAsStream("breakfast_menu.xml"));

File homeDirectory = new File(System.getProperty("user.home"));
File outputFile = new File(homeDirectory, "breakfast_menu_1.html");

try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
    transformer.transform(xmlSource, new StreamResult(outputStream));
}

The result of the transformation is shown below:

XSLT result

The following is an example of how a similar template can be defined using Mustache-style syntax. It is much simpler and easier to read than the previous version, and can be edited using any HTML-aware text editor:

<!-- Breakfast Menu -->
<html>
    <body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
        <!-- {{#food*}} -->
            <div style="background-color:teal;color:white;padding:4px">
                <span style="font-weight:bold">{{name}} - </span>
                {{price}}
            </div>
            <div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
                <p>
                    {{description}}
                    <span style="font-style:italic"> ({{calories}} calories per serving)</span>
                </p>
            </div>
        <!-- {{/food*}} -->
    </body>
</html>

This code applies the template to the XML document using TemplateEncoder:

DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

Document document;
try (InputStream inputStream = getClass().getResourceAsStream("breakfast_menu.xml")) {
    document = documentBuilder.parse(inputStream);
}

TemplateEncoder templateEncoder = new TemplateEncoder(getClass().getResource("breakfast_menu.html"));

File homeDirectory = new File(System.getProperty("user.home"));
File outputFile = new File(homeDirectory, "breakfast_menu_2.html");

try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
    templateEncoder.write(new ElementAdapter(document.getDocumentElement()), outputStream);
}

The result is identical to the XSLT version:

Mustache result

Simplicity isn’t the only advantage of the Mustache-style approach. On average, the XSLT Transformer version takes about 350ms to run, compared to about 30ms for TemplateEncoder – about a 10x difference.

For more information about HTTP-RPC, TemplateEncoder, and ElementAdapter, see the project README.