Transforming XML with Mustache Templates
As of HTTP-RPC 7.4, the TemplateEncoderclass can be used to transform XML using a lightweight syntax similar to Mustache.
Join the DZone community and get the full member experience.
Join For FreeAlthough 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, theTemplateEncoder
class can be used to transform XML using a lightweight syntax similar to Mustache. The newElementAdapter
class provides access to the contents of an XML DOM Element
via the Map
interface. The contents of this map can be easily transformed into 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>
Thejavax.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:
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:
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.
Published at DZone with permission of Greg Brown, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments