Search code examples
xmlantsaxonxslt-3.0fileset

How to pass xml fileset into Ant xslt task as input with one xml output file saxon xslt 3.0


I have a folder of XML files. I'd like to copy some lines from each file into one XML file that I can then use as a lookup. I have an xslt file and an ANT build file but I'm sure there has to be a simple way to do this.

Input files look likes this:

<engine_config>
    <eng_family>RK2-1020</eng_family>
    <eng_model>RK2-1020</eng_model>
    <cd_gek>gek1000</cd_gek>
    <version_no>1000</version_no>
    <version_name>all</version_name>
    <display_name>RK2 1000</display_name>
    ...lots of other info I don't need
</engine_config>

Desired result:

<gek-lookup>
   <engine_config>
      <eng_family>RK2-1020</eng_family>
      <eng_model>RK2-1020</eng_model>
      <display_name>RK2 1000</display_name>
      <cd_gek>gek1000</cd_gek>
   </engine_config>
   <engine_config>
      <eng_family>RK2-1021</eng_family>
      <display_name>RK2 1001</display_name>
      <cd_gek>gek1001</cd_gek>
   </engine_config>
   etc...
</gek-lookup>

XSLT:

<xsl:template match="/">
   <gek-lookup>
       <xsl:apply-templates />
   </gek-lookup>
</xsl:template>
    
<xsl:template match="*">
   <xsl:copy>
     <xsl:copy-of select="engine_model | eng_family"/>
     <xsl:copy-of select="display_name"/>
     <xsl:copy-of select="cd_gek"/>
   </xsl:copy>
</xsl:template>

This task is creating an XML output file for each input file, but I want all the results to be written to one output file:

<target name="GenerateFileIndex">    
  <xslt 
    basedir="configuration-files"
    destdir="configuration-files\working"
    classpath="${saxon.jar}"
    includes="*.xml,*.XML"
    excludes="configurations*"
    style="build_lookup_file.xsl" 
    extension=".xml"
    out="configuration-files\working\configurations.xml"
    force="true">
    <factory name="net.sf.saxon.TransformerFactoryImpl"/>
  </xslt>
</target>

I created this fileset but I don't know how to properly use the fileset with the xslt task when there is only one output file.

<fileset id="xmlfileset" dir="${config.dir}" casesensitive="no">
    <include name="*.XML" />
    <exclude name="configurations.xml"/>
</fileset>

This almost works, but it's very slow and it overwrites the previous entry in the output file:

<target name="GenerateFileIndex">
    <mkdir dir="${output.dir}"/>
    <apply executable="java" failonerror="true">
      <arg value="-cp"/>
      <arg value="${saxon.jar}"/>
      <arg value="net.sf.saxon.Transform"/>
      <srcfile/>
      <fileset refid="xmlfileset"/>
      <arg value="-xsl:${xslt}"/>
      <arg value="-o:${output.dir}${file.separator}${gek.index}"/>
    </apply>
  </target> 

Any suggestions are appreciated.

EDIT I passed the files as a string per Dr. Kay's suggestion. The only issue, I needed to put something for the in parameter so I hardcoded a file that was in the folder:

<target name="GenerateFileIndex">
    <pathconvert pathsep="," property="xmlflist" refid="xmlfileset"/>
    <mkdir dir="${output.dir}"/>
    <xslt in="${config.dir}/gek108729.xml" extension=".xml"
      includes="*.xml,*.XML"
      excludes="configuration*"
      destdir="${output.dir}" style="${xslt}" out="${output.dir}\${gek.index}">
      <param name="xml-config-files" expression="${xmlflist}"/>
    </xslt>
  </target>

Stylesheet:

<xsl:param name="xml-config-files" as="xs:string*"/>
    
    <xsl:template match="/">
        <gek-lookup>
            <xsl:for-each select="tokenize($xml-config-files,',')">
                <xsl:apply-templates select="doc(concat('file:///', translate(., '\', '/')))/*"/>
            </xsl:for-each>
        </gek-lookup>
    </xsl:template>

<xsl:template match="/*">
        <xsl:copy>
            <xsl:copy-of select="eng_family"/>
            <xsl:copy-of select="engine_model"/>
            <xsl:copy-of select="cd_gek"/>
            <xsl:copy-of select="version_no"/>
            <xsl:copy-of select="display_name"/>
        </xsl:copy>
    </xsl:template>

Solution

  • I'm not aware of any way of passing an Ant fileset directly into a Saxon transformation, but you can certainly pass the name of a directory as a stylesheet parameter, and use this as an argument to the collection() function. Saxon's handling of collections is described at https://www.saxonica.com/documentation12/index.html#!sourcedocs/collections