Reputation: 2401
We have a large number of xml configuration files that we want merged into one master version at build time. Smaller config files are easier to maintain and one large file loads faster so I imagined this to be a popular build transformation process that I would find lots of good examples of on the net.
I was able to find some good solutions to one part of the problem here at StackOverflow but they all rely upon knowing the names of the xml files that need merging up front. This seems like an unnecessary overhead to me. It should be possible to write a build script which can dynamically calculate which input xml files are needed.
Unfortunately, the only way I could find to achieve this was a bit of a hack. It works like this,
Here's the ant script
<taskdef name="xml-dir-list"
classname="net.matthaynes.xml.dirlist.AntFileListing"
classpath="antlib/xml-dir-listing.0.1.jar;
antlib/jakarta-regexp-1.5.jar;antlib/log4j-1.2.14.jar"/>
<macrodef name="build-plugin-xml" description="todo">
<attribute name="pluginName"/>
<xml-dir-list depth="0" verbose="false"
srcDir="${src.dir}/@{pluginName}/forms/" includesRegEx="\.xml$"
destFile="${src.dir}/@{pluginName}/forms/fileList.xml"/>
<xslt in="${src.dir}/forms/fileList.xml"
out="${src.dir}/@{pluginName}/@{pluginName}_extn.yuix
style="${src.dir}/@{pluginName}/forms/extn.yuix.xsl" />
<delete file="${src.dir}/@{pluginName}/forms/fileList.xml"/>
</macrodef>
And here's the stylesheet,
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<Forms applicationId="YFSSYS00011">
<GlobalExtensions>
<Tasks/>
</GlobalExtensions>
<xsl:apply-templates select="directory/file"/>
</Forms>
</xsl:template>
<xsl:template match="file">
<xsl:copy-of select="document(@name)/Forms/Form"/>
</xsl:template>
</xsl:stylesheet>
Has anyone found a simpler way to achieve this dynamic discovery of what files to merge in XSLT? It's not surprising that XSLT cannot read directories directly, but I was hoping to find a simpler way to pass in a list of file names than through another xml file.
Dimitre's solution worked great once I'd made a few extra tweaks to the ant script,
<taskdef name="saxon-xslt" classname="net.sf.saxon.ant.AntTransform"
classpath="antlib/saxon9.jar;antlib/saxon9-ant.jar"/>
[...]
<macrodef name="build-plugin-xml" description="todo">
<attribute name="pluginName"/>
<saxon-xslt
in="build.xml"
out="${compca.src.dir}/temp/@{pluginName}/@{pluginName}_extn.yuix"
style="antscripts/extn.yuix.xsl">
<param name="formsDir"
expression="${compca.src.dir}/@{pluginName}/forms/"/>
</saxon-xslt>
</macrodef>
and the xsl stylesheet (which I moved)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="formsDir" />
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<Forms applicationId="YFSSYS00011">
<GlobalExtensions>
<Tasks/>
</GlobalExtensions>
<xsl:apply-templates select=
"collection(
concat('file:///',
$formsDir,
'?select=*.yuix;recurse=yes;on-error=ignore'
)
)/*
"/>
</Forms>
</xsl:template>
<xsl:template match="file">
<xsl:copy-of select="/Forms/Form"/>
</xsl:template>
</xsl:stylesheet>
Those tweaks were just around getting Saxon9 to load and setting the directory with a parameter.
Upvotes: 5
Views: 5987
Reputation: 243539
Has anyone found a simpler way to achieve this dynamic discovery of what files to merge in XSLT? It's not surprising that XSLT cannot read directories directly, but I was hoping to find a simpler way to pass in a list of file names than through another xml file.
Dynamic discovery and processing of XML files can be done using XPath 2/0/XSLT 2.0. More specifically, one has to use the XPath 2.0 collection()
function.
For details, see my answer to this question.
So, if ANT can use a suitable XSLT 2.0 processor, (I would recommend using Saxon), the problem has a complete solution, using the standard collection()
function.
Upvotes: 6