XProc:使用中间文件进行多次XSLT转换

时间:2012-05-17 13:24:16

标签: xml xproc

我需要使用中间XML文件进行多次XSLT转换。 (我需要文件,真实案例有点棘手,因为后面的步骤会加载中间文件)

first.xml ------------>   intermediate.xml ------------> final.xml
          first.xsl                         final.xsl

我想创建一个XProc pipleline。我曾尝试编写以下代码,但这给了我一个错误:

SCHWERWIEGEND: runxslt.xpl:26:44:err:XD0011:Could not read: intermediate.xml 
17.05.2012 15:15:35 com.xmlcalabash.drivers.Main error
SCHWERWIEGEND: It is a dynamic error if the resource referenced by a p:document element does not exist, cannot be accessed, or is not a well-formed XML document.
17.05.2012 15:15:35 com.xmlcalabash.drivers.Main error
SCHWERWIEGEND: Underlying exception: net.sf.saxon.s9api.SaxonApiException: I/O error reported by XML parser processing file:/<somepath>/intermediate.xml:
/<somepath>/intermediate.xml (No such file or directory)

(其中SCHWERWIEGEND的含义类似于FATAL)显然文件intermediate.xml尚未写入。

这是我用过的xpl文件:

<?xml version="1.0" encoding="UTF-8"?>
<p:declare-step xmlns:p="http://www.w3.org/ns/xproc"
  xmlns:c="http://www.w3.org/ns/xproc-step" version="1.0">

  <p:input port="source">
    <p:document href="first.xml"/>
  </p:input>

  <p:output port="result" sequence="true">
    <p:empty/>
  </p:output>

  <p:xslt name="first-to-intermediate">
    <p:input port="stylesheet">
      <p:document href="first.xsl"/>
    </p:input>
    <p:input port="parameters">
      <p:empty/>
    </p:input>
  </p:xslt>

  <p:store href="intermediate.xml" />

  <p:xslt>
    <p:input port="source">
      <p:document href="intermediate.xml"/>
    </p:input>
    <p:input port="stylesheet">
      <p:document href="final.xsl"/>
    </p:input>
    <p:input port="parameters">
      <p:empty/>
    </p:input>
  </p:xslt>

  <p:store href="final.xml"/>

</p:declare-step>

仅为了完整性:这些是转换文件:

source.xml:

<root>
  <element name="A" />
</root>

first.xsl:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">
  <xsl:output indent="yes"/>

  <xsl:template match="root">
    <root>
      <xsl:apply-templates/>
    </root>
  </xsl:template>
  <xsl:template match="element">
    <intermediate name="A" />
  </xsl:template>

</xsl:stylesheet>

final.xsl:

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

  <xsl:template match="root">
    <root>
      <xsl:apply-templates/>
    </root>
  </xsl:template>
  <xsl:template match="intermediate">
    <final name="A" />
  </xsl:template>

</xsl:stylesheet>

以下是关于实际应用的说明(当然,上面是简化)。

  1. 第一步:将源转换为更适合我处理的内容。输出:companies.xml
  2. 从第1步获取输出并从中创建索引文件(index.xml)。索引文件必须手动编辑。
  3. 第三步是合并步骤1和2创建的文件并创建最终的xml(final.xml
  4. 索引文件必须写入磁盘,我必须能够自己运行最后一步(这是一个不同的问题 - 我会为此写一个不同的管道)

    companies.xml的输出(步骤1)是可选的,它可以保存在内存中(但可能会变大)。

1 个答案:

答案 0 :(得分:2)

我不确定为什么XMLCalabash在这里不起作用。我认为逻辑原则上应该工作,但显然XMLCalabash暂时将文件写入磁盘直到以后,甚至可能直到最后。不知道为什么。

但是有一个优雅的解决方案,因为您不需要在继续处理之前存储中间结果。事实上,最好不要使用硬编码的负载和存储。相反,请使用以下内容:

<?xml version="1.0" encoding="UTF-8"?> 
<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" 
  xmlns:c="http://www.w3.org/ns/xproc-step" version="1.0"> 

  <p:input port="source" sequence="true"/> 
  <p:input port="parameters" kind="parameter"/>
  <p:output port="result" sequence="true"/> 

  <p:xslt name="first-to-intermediate"> 
    <p:input port="stylesheet"> 
      <p:document href="first.xsl"/> 
    </p:input> 
  </p:xslt> 

  <p:xslt> 
    <p:input port="stylesheet"> 
      <p:document href="final.xsl"/> 
    </p:input> 
  </p:xslt> 

</p:declare-step> 

它需要对XMLCalabash稍有不同的调用。这样称呼:

java -jar Calabash.jar -i source=first.xml -o result=final.xml runxslt.xpl

使用-i将输入源绑定到输入文件,但是从脚本外部,因此不需要硬编码。与-o类似,您将输出重定向到目标文件。

我还在代码中添加了一个'参数'输入,它自动连接到p:xslt的输入。这样您就不需要指定p:empty的那些。它还允许将命令行中的参数值传递给那些xslt。

因为我删除了p:store,所以第二个p:xslt的'source'输入也不是必需的。第一个p:xslt的结果默认直接进入下一步的(主)源输入。

- 编辑 -

要详细说明我自己的评论,你可以做一个p:store 重复使用第一个p:xslt的输出两次而不从磁盘加载中间文档。你可以这样做:

<?xml version="1.0" encoding="UTF-8"?> 
<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" 
  xmlns:c="http://www.w3.org/ns/xproc-step" version="1.0"> 

  <p:input port="source" sequence="false"/> 
  <p:input port="parameters" kind="parameter"/>
  <p:output port="result" sequence="false"/> 

  <p:xslt name="first-to-intermediate"> 
    <p:input port="stylesheet"> 
      <p:document href="first.xsl"/> 
    </p:input> 
  </p:xslt> 

  <p:store href="intermediate.xml"/>

  <p:xslt> 
    <p:input port="source"> 
      <p:pipe step="first-to-intermediate" port="result"/> 
    </p:input> 
    <p:input port="stylesheet"> 
      <p:document href="final.xsl"/> 
    </p:input> 
  </p:xslt> 

</p:declare-step> 

请注意,我在declare-step的输入和输出上将sequence = true更改为false。存储中间结果序列需要额外注意。这应该可以防止错误。

HTH!