Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamically concatenating xml files with ant and xslt

Tags:

xml

xslt

ant

saxon

Question

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,

  1. Use a random ant task I stole of the internet to write a directory listing into an xml file.
  2. Feed the xml file into an xslt transformation that can then load the other directory listing referenced xml files and concatenate them.
  3. Delete the temporary xml file containing the directory listing.

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.

Implemented Solution

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.

like image 965
Caoilte Avatar asked Feb 01 '09 16:02

Caoilte


1 Answers

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.

like image 83
Dimitre Novatchev Avatar answered Nov 19 '22 01:11

Dimitre Novatchev