フィルタを使ってxsltのimport/includeタグのパスを変える

はじめに

antのフィルタを使ってxsltの中でimport/includeするファイルのパスを実行時に変える.具体的にはこんなこと.

<xsl:import href="ここのパスを変える/ファイル名.xsl"/>

importもincludeもやり方は同じなので,以下importのみ記載する.

結論

  1. antのフィルタを使う.
  2. import/includeのhrefに記載するパスにフィルタのトークン(@で囲んだ文字列)を書く.
  3. filterを指定してcopyタスクを実行.
  4. paramでは上手くいかない.

やってみる

こんなことをやってみる.

  1. xslファイルを二つ(test.xslとframework.xsl),xmlファイルを一つ(in.xml)を作る.
  2. 一時フォルダを作成してframework.xslを一時フォルダにコピー.
  3. test.xslの中でframework.xslをimport.パスは上記2で作った一時フォルダ.
  4. framework.xslの中でin.xmlの内容を出力.

一時フォルダにコピーしたframework.xslが,正しくtest.xslにimportできれば,in.xmlの内容が出てくるはず.

xslt(test.xsl)の記載(import/includeタクを記載する側)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:import href="@TMP.PATH@/framework.xsl"/>
  <!-- importするファイルのあるパスをトークン(@で囲んだ文字列)として記載 -->
  <xsl:output method="text"/>
</xsl:stylesheet>

xslt(framework.xsl)の記載(import/includeタクに記載される側)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:template match="/">
    <xsl:value-of select="."/>
  </xsl:template>
</xsl:stylesheet>

in.xmlの記載

<?xml version="1.0" encoding="UTF-8"?>
<root>
  this is test.
</root>

build.xmlの記載

こんなことを行う.

  1. 一時フォルダを作成.
  2. 一時フォルダにframework.xsl(importされるファイル)をコピー.
  3. test.xsl(importを記載するファイル)の中のパス名をフィルタで一時フォルダに変える.
  4. xsltファイルを適用.
<project name="sample" default="run">
  <property environment="env"/>

  <target name="run">
    <!-- 一時フォルダを作成 -->
    <tempfile destdir="${env.TMP}" property="tmp.path.windows"/>
    <echo message="${tmp.path.windows}"/>
    <mkdir dir="${tmp.path.windows}"/>

    <!-- 
      cygwinを使っているので,パス区切文字の変換(Windows形式からUNIX形式へ)が必要.
      パスの区切文字がWindows形式(バックスラッシュ)だとエラーになるので,UNIX形式(スラッシュ)に変える 
      -->
    <pathconvert property="tmp.path" targetos="unix">
      <path>
        <pathelement location="${tmp.path.windows}"/>
      </path>
    </pathconvert>    
    <echo message="${tmp.path}"/>

    <!-- 一時フォルダにframework.xsl(importされるファイル)をコピー -->
    <copy file="framework.xsl" tofile="${tmp.path}/framework.xsl"/>

    <!-- パス名をフィルタで一時フォルダに変える -->
    <copy file="test.xsl" tofile="${tmp.path}/test.xsl">
      <filterset>
        <filter token="TMP.PATH" value="${tmp.path}"/>
      </filterset>
    </copy>

    <!-- スタイルシートを適用 -->
    <xslt style="${tmp.path}/test.xsl" in="in.xml" out="out.txt"/>
  </target>
</project>

実行結果

$ ant
Buildfile: C:\Users\foo\Desktop\ant.test\filter\build.xml

run:
     [echo] C:\Users\foo\AppData\Local\Temp\null1986596527
    [mkdir] Created dir: C:\Users\foo\AppData\Local\Temp\null1986596527
     [echo] C:/Users/foo/AppData/Local/Temp/null1986596527
     [copy] Copying 1 file to C:\Users\foo\AppData\Local\Temp\null1986596527
     [copy] Copying 1 file to C:\Users\foo\AppData\Local\Temp\null1986596527

$ cat out.txt

        this is test.

変数は使えないのか

paramタスクを使っても,antからxsltに値を渡せる.これでパスの変換はできないのだろうか.結論から言うとできなかった.import/include実行時には変数に値が入っていないようだ.

xslt(test.param.xsl)

antから引数を受取るためにparamタグを指定する.importはstylesheetタグ直下にないといけないので,paramタグはimportタグの後ろになる.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:import href="$tmp.path/framework.xsl"/> <!-- importはstylesheet直下にないとエラー -->
  <xsl:param name="tmp.path"/> <!-- antからの引数を受け取る -->

  <xsl:output method="text"/>
</xsl:stylesheet>

build.param.xml

xsltタスクの中でparamタスクを使ってxsltにパスを渡す.

<project name="sample" default="run">
  <property environment="env"/>

  <target name="run">
    <!-- 一時フォルダを作成 -->
    <tempfile destdir="${env.TMP}" property="tmp.path.windows"/>
    <echo message="${tmp.path.windows}"/>
    <mkdir dir="${tmp.path.windows}"/>

    <!-- パスの区切り文字を変更 -->
    <pathconvert property="tmp.path" targetos="unix">
      <path>
        <pathelement location="${tmp.path.windows}"/>
      </path>
    </pathconvert>    
    <echo message="${tmp.path}"/>

    <!-- 一時フォルダにframework.xsl(importされるファイル)をコピー -->
    <copy file="framework.xsl" tofile="${tmp.path}/framework.xsl"/>

    <!-- スタイルシートを適用.paramでxsltへ値を渡す -->
    <xslt style="test.param.xsl" in="in.xml" out="out.param.txt">
      <param name="tmp.path" expression="${tmp.path}"/> <!-- xsltへ値を渡す -->
    </xslt>
  </target>
</project>

実行結果

引数tmp.pathは具体値に変換されず,エラーになる.

$ ant -f build.param.xml
Buildfile: C:\Users\foo\Desktop\ant.test\filter\build.param.xml

run:
     [echo] C:\Users\foo\AppData\Local\Temp\null1353224696
    [mkdir] Created dir: C:\Users\foo\AppData\Local\Temp\null1353224696
     [echo] C:/Users/foo/AppData/Local/Temp/null1353224696
     [copy] Copying 1 file to C:\Users\foo\AppData\Local\Temp\null1353224696
     [xslt] Processing C:\Users\foo\Desktop\ant.test\filter\in.xml to C:\Users\foo\Desktop\ant.test\filter\out.param.txt
     [xslt] Loading stylesheet C:\Users\foo\Desktop\ant.test\filter\test.param.xsl
     [xslt] : Error! スタイルシートをコンパイルできませんでした
     [xslt] : Fatal Error! C:\Users\foo\Desktop\ant.test\filter\$tmp.path\framework.xsl (指定されたパスが見つかりません。) Cause: java.io.FileNotFoundException: C:\Users\foo\Desktop\ant.test\filter\$tmp.path\framework.xsl (指定されたパスが見つかりません。)
     [xslt] Failed to process C:\Users\foo\Desktop\ant.test\filter\in.xml

BUILD FAILED
C:\Users\foo\Desktop\ant.test\filter\build.param.xml:18: Fatal error during transformation using C:\Users\foo\Desktop\ant.test\filter\test.param.xsl: C:\Users\foo\Desktop\ant.test\filter\$tmp.path\framework.xsl (指定されたパスが見つかりません。)

include版

paramがimportよりも後ろにあるからエラーになるのかもしれない.includeで試してみた.

xsl(test.param2.xsl)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:param name="tmp.path"/>
  <xsl:include href="$tmp.path/framework.xsl"/> <!-- includeはファイルの途中にあってもOK -->

  <xsl:output method="text"/>
</xsl:stylesheet>

build.param2.xml

<project name="sample" default="run">
  <property environment="env"/>

  <target name="run">
    <tempfile destdir="${env.TMP}" property="tmp.path.windows"/>
    <echo message="${tmp.path.windows}"/>
    <mkdir dir="${tmp.path.windows}"/>

    <pathconvert property="tmp.path" targetos="unix">
      <path>
        <pathelement location="${tmp.path.windows}"/>
      </path>
    </pathconvert>    
    <echo message="${tmp.path}"/>

    <copy file="framework.xsl" tofile="${tmp.path}/framework.xsl"/>

    <xslt style="test.param2.xsl" in="in.xml" out="out.param2.txt">
      <param name="tmp.path" expression="${tmp.path}"/>
    </xslt>
  </target>
</project>

実行結果

import同様,引数tmp.pathは具体値に変換されず,エラーになる.

$ ant -f build.param2.xml
Buildfile: C:\Users\foo\Desktop\ant.test\filter\build.param2.xml

run:
     [echo] C:\Users\foo\AppData\Local\Temp\null693261028
    [mkdir] Created dir: C:\Users\foo\AppData\Local\Temp\null693261028
     [echo] C:/Users/foo/AppData/Local/Temp/null693261028
     [copy] Copying 1 file to C:\Users\foo\AppData\Local\Temp\null693261028
     [xslt] Processing C:\Users\foo\Desktop\ant.test\filter\in.xml to C:\Users\foo\Desktop\ant.test\filter\out.param2.txt
     [xslt] Loading stylesheet C:\Users\foo\Desktop\ant.test\filter\test.param2.xsl
     [xslt] : Error! スタイルシートをコンパイルできませんでした
     [xslt] : Fatal Error! C:\Users\foo\Desktop\ant.test\filter\$tmp.path\framework.xsl (指定されたパスが見つかりません。) Cause: java.io.FileNotFoundException: C:\Users\foo\Desktop\ant.test\filter\$tmp.path\framework.xsl (指定されたパスが見つかりません。)
     [xslt] Failed to process C:\Users\foo\Desktop\ant.test\filter\in.xml

BUILD FAILED
C:\Users\foo\Desktop\ant.test\filter\build.param2.xml:18: Fatal error during transformation using C:\Users\foo\Desktop\ant.test\filter\test.param2.xsl: C:\Users\foo\Desktop\ant.test\filter\$tmp.path\framework.xsl (指定されたパスが見つかりません。)

Total time: 0 seconds

環境

  • Windows 10 Pro
  • cygwin 2.9.0
  • ant 1.10.5
  • Saxon-HE 9.9.1.1J
  • OpenJDK 12.0.1