How to Generate an XSLT Transform Using Groovy StreamingMarkupBuilder
Groovy and XSLT are two great options for working with XML. While both have their uses, I was curious about combining them to generate an XSLT transform with a Groovy script. It turns out that doing this is pretty straight forward, with a couple of minor caveats.
Namespaces
XSLT commands like template and value-of are defined in the “http://www.w3.org/1999/XSL/Transform” namespace (commonly aliased with xsl). Therefore, generating XML with XSLT commands requires using a namespace.
Luckily, Groovy’s StreamingMarkupBuilder has support for namespaces via a special markup object called mkp, which contains declareNamespace() function:
def xmlWithNamespace = new StreamingMarkupBuilder().bind {
mkp.declareNamespace(abc: "some namespace")
}
Once declared, creating an node using the namespace is simply a matter of using the “.” notation:
namespace.nodeName {
namespace.anotherNodeName {
...
}
}
Unfriendly Casing Style
Like most other W3C standards, XSLT uses “lower-case-with-hyphens” casing style for its keywords: value-of, apply-templates, and so on. This creates a slight complication for Groovy because words with hyphens are not valid symbols (i.e. xsl.value-of{} won’t work). Instead, you have to surround these terms in quotes: xsl."value-of"{}.
Final Result
So, here’s a Groovy script to generate a simple XSLT transform:
def xmlWithNamespace = new StreamingMarkupBuilder().bind {
mkp.declareNamespace(mynamespace: "http://www.w3.org/1999/XSL/Transform")
}
xsl.stylesheet(version:"2.0") {
xsl.template(match:"/") {
html {
head {
title("list of items")
}
}
body {
h1("items") {
ul {
xsl."apply-templates"(select:"items/item")
}
}
}
}
xsl.template(match:"item") {
li {
xsl."value-of"(select:"name")
}
}
}
}
And here’s the XSLT the script above generates:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head>
<title>list of items</title>
</head>
</html>
<body>
<h1>
<ul>
<xsl:apply-templates select="items/item"/>
</ul>
</h1>
</body>
</xsl:template>
<xsl:template match="item">
<li>
<xsl:value-of select="name"/>
</li>
</xsl:template>
</xsl:stylesheet>