XSLT条件包含外部文件

6
我想在XSLT中执行条件包含,但xsl:include是一个顶级元素。你只能在模板内使用xsl:if或xsl:choose。是否有任何一种黑客或解决方法可以允许对外部文件进行条件包含?我尝试使用document()函数,但它无法加载我的外部文件(可能是因为它不符合某些规则,使其“有效”)。
我的外部xml文件是一堆xslt代码片段。根据主XSLT文件中变量的值,应该将相应的代码从外部文件中“复制/粘贴”到指定位置(类似于C或PHP中的条件包含)。
我的主XSLT文件的流程应按以下方式进行:
$configurationMode
if ( $configurationMode = Standard ) { xsl:include="standard.xml" } else { xsl:include="alt.xml" }

显然,我不能像上面那样简单地完成它,这就是为什么我要问是否有黑客或变通方法的原因。
8个回答

6
XSLT 1.0无法实现此操作,而 XSLT 2.0 仅能在非常有限的情况下使用 use-when 属性来达成目的。

还有一些非 XSLT 的方法可以实现所需的动态修改 xsl:includexsl:import 指令。

其中一种方法是将 XSLT 样式表作为 XmlDocument 加载,并使用可用的 DOM 方法访问和修改属性,将 href 属性设置为所需的值。然后从内存中修改过的 XMLDocument 包含的 XSLT 样式表开始转换。


3
据我所知,“include”是在xml解析器解析和编译样式表时发生的。这意味着在包含被处理之前,任何逻辑或表达式计算都不能发生,因此没有办法将其作为条件进行处理。您需要在样式表外部实现条件行为。
请查看Mike Kay的以下评论:http://www.dpawson.co.uk/xsl/sect2/N4760.html#d6065e100 特别是这段话:
“这件事已经被提出多次了。在以前的讨论中,我们得出结论,用户试图编写一个通用的样式表G,然后通过有条件地包含特定目的样式表A或B来进行专门化。满足此要求的方法是让A和B包含G,而不是相反,并且在启动转换时有条件地选择A或B作为主样式表。”

3

尝试颠倒结构:如果你有两个专用模块pink.xsl和blue.xsl,以及一个通用模块baby.xsl,则不要尝试将pink.xsl或blue.xsl之一导入/包含到baby.xsl中,而是将pink.xsl或blue.xsl用作顶级入口样式表,并让这两个模块都导入baby.xsl。这是设计时的预期使用方式,而不是黑客或解决方法。

另外,根据您的情况描述:“我的外部XML文件是一堆XSLT代码片段”,在您的情况下更好的方法可能是使用XSLT转换作为单独步骤从这些片段中组装样式表,而不是使用xsl:include / xsl:import。


1

不确定这是否适用于您的情况,但我还是要提出来。

我以前做过类似的事情,但与其使用条件包含,我根据xsl:when评估的结果在两个文件中的一个中调用模板。如果您不想在您的情况下使用模板,请忽略此答案。

例如,假设“父”xslt文件称为root.xslt,两个有条件使用的文件分别为child1.xsltchild2.xslt。假设我想针对名为status的节点的值运行条件逻辑。如果该值为“CURRENT”,则我想在child1.xslt中调用名为isCurrent的模板,否则在child2.xslt中调用名为isNotCurrent的模板。为简洁起见,在每种情况下,我只是传递了根节点和所有子节点给模板。

它看起来会像这样:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:import href="child1.xslt"/>
    <xsl:import href="child2.xslt"/>

    <xsl:template match="/">
      <xsl:choose>
        <xsl:when test="rootNode/status = 'CURRENT'">
          <!-- template isCurrent defined in child1.xslt -->
          <xsl:apply-templates select="rootNode" mode="isCurrent" />
        </xsl:when>
        <xsl:otherwise>
          <!-- template isNotCurrent defined in child2.xslt -->
          <xsl:apply-templates select="rootNode" mode="isNotCurrent" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

希望这至少能够有所帮助。


0

我发现在XSLT 2.0中,可以使用use-when功能有条件地包含文件的一种方法。

您需要创建一个系统属性,在运行时通过应用程序传递,例如在我的情况下,我正在使用Java,因此我使用命令创建了“Debug”属性来运行我的应用程序:

java -DDebug=yes myAppPath

然后在XSLT中使用此属性来包含条件文件。例如

<xsl:include href="file1.xslt" use-when="system-property('DEBUG') = 'yes'"/>
<xsl:include href="file2.xslt" use-when="system-property('DEBUG') != 'yes'"/>

就像这样。

不知道是否适用于所要求的场景,但是在XSLT 2.0中有一种方法可以实现。 在XSLT 3.0中,还添加了对局部变量的支持。因此,在那里可以很容易地使用局部变量来完成这个任务。

谢谢, 愉快编码


0
除了已经提到的内容之外,可能的解决方案是将补充文件制作为纯粹的、提供内容的 XML 文件(而不是 XSLT 文件)。这样,您可以使用 XPath 的 document() 函数将它们包含进来(这将在运行时而不是编译时进行评估)。
然后,您可以根据加载的 XML 文档的内容改变转换的行为;但是,您不能在包含的文档中提供可执行的 XSLT 片段。
这取决于您的用例是否是一个解决方案——如果附加文档对您的转换的控制流有很大影响,您不希望将它们定义为纯 XML,因为您基本上会重新实现类似 XSLT 的东西。然而,当您的文档作为配置文件时,您可能想考虑将它们提供为纯 XML。
当您遇到 document() 的问题时,请在文件上使用 XML 验证器。错误意味着您的文件不是有效的 XML。

0
<xsl:variable name="CONDITIONAL" select="ELEMENT/CONDITION"/>
<xsl:variable name="DOCUMENTNAME" select="document(concat($CONDITIONAL,'-DOCUMENT.xml'))/ELEMENT"/>

这个例子展示了我如何有条件地使用外部文件。我在基本的XML中设置了一个标志,XSL将其用作变量来帮助指定要拉入模板的其他文件的名称。一旦定义了该变量,我就可以使用它来将其他XML数据拉入生成的输出中。concat命令强制将数据组合在一起以给出文件名。

因此,当我想要从外部XML文件中获取信息时,我使用'$DOCUMENTNAME'来引用外部数据。


0

通过添加静态参数,在XSLT 3.0中现在可以实现这一点。静态参数可以在xsl:includeuse-when属性中使用。

现在我们可以声明具有默认值false()的参数,然后在运行时覆盖我们需要的参数...

<xsl:param name="someparam" as="xs:boolean" select="false()" 
  static="yes" required="no"/>  
<xsl:include href="include_me.xsl" use-when="$someparam"/>

这里是一个完整的工作示例,已经使用Saxon-HE v9.7进行了测试(也已经使用Saxon-PE 9.5进行了测试)。

XML输入(test.xml)

<doc>
    <foo/>
</doc>

主要 XSLT 3.0(test_main.xsl)

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="inc1" as="xs:boolean" select="false()" 
    static="yes" required="no"/>
  <xsl:param name="inc2" as="xs:boolean" select="false()" 
    static="yes" required="no"/>

  <xsl:include href="test_inc1.xsl" use-when="$inc1"/>
  <xsl:include href="test_inc2.xsl" use-when="$inc2"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

首个可能包含 XSLT 3.0 的文件(test_inc1.xsl)

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="foo">
    <xsl:copy>INCLUDE FILE 1!!!</xsl:copy>
  </xsl:template>

</xsl:stylesheet>

第二个可能包含的 XSLT 3.0(test_inc2.xsl)

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="foo">
    <xsl:copy>INCLUDE FILE 2!!!</xsl:copy>
  </xsl:template>

</xsl:stylesheet>

命令行(将inc2设置为true)

java -cp "saxon9he.jar" net.sf.saxon.Transform -s:"test.xml" -xsl:"test_main.xsl" inc2="true"

输出

<doc>
   <foo>INCLUDE FILE 2!!!</foo>
</doc>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接