【问题标题】:XSLT conditional include of external file外部文件的 XSLT 条件包含
【发布时间】:2012-01-24 17:23:49
【问题描述】:

我想在 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" }

显然我不能像上面那样简单地做到这一点,因此我问是否有破解或解决方法。

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    这不能在 XSLT 1.0 中完成,但可以在 XSLT 2.0 中使用use-when 属性完成(在非常有限的范围内)

    存在实现对xsl:includexsl:import 指令的所需动态更改的非xslt 方法。

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

    【讨论】:

      【解决方案2】:

      据我了解,“包含”发生在 xml 解析器解析和编译样式表时。这意味着在处理包含之前不会发生任何逻辑或表达式评估,因此无法使其成为有条件的。您需要使条件行为发生在样式表之外。

      看看这个http://www.dpawson.co.uk/xsl/sect2/N4760.html#d6065e100

      Mike Kay 的这条评论特别有帮助:

      这已被多次提出。在上一个线程上,我们来了 得出的结论是用户试图编写一个通用的 样式表 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.

        【讨论】:

          【解决方案4】:

          不确定这是否适用于您的场景,但无论如何我都会把它扔掉。

          我过去做过类似的事情,但我没有做条件包含,而是根据 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>
          

          希望这至少会有所帮助。

          【讨论】:

            【解决方案5】:

            除了已经说过的内容之外,一种可能的解决方案是使补充文件成为普通的、提供内容的 XML 文件(而不是 XSLT 文件)。这样,您可以将它们包含在 XPath 的 document() 函数中(将在运行时而不是编译时进行评估)。

            然后您可以根据加载的 XML 文档的内容更改转换的行为;但是,您不能在包含的文档中提供可执行的 XSLT 片段。

            这是否是一个解决方案取决于您的用例 - 如果附加文档对转换的控制流有很大影响,您不想在纯 XML 中定义它们,因为您基本上会重新-实现类似 XSLT 的东西。但是,当您的文档用作一种配置文件时,您可能需要考虑将它们提供为纯 XML。

            当您遇到document() 问题时,请对您的文件使用 XML 验证器。错误意味着您的文件不是有效的 XML。

            【讨论】:

              【解决方案6】:
              <xsl:variable name="CONDITIONAL" select="ELEMENT/CONDITION"/>
              <xsl:variable name="DOCUMENTNAME" select="document(concat($CONDITIONAL,'-DOCUMENT.xml'))/ELEMENT"/>
              

              这个例子是我如何有条件地使用外部文件。我在基本 XML 中有一个标志,XSL 使用它作为变量来帮助指定要拉入模板的另一个文件的名称。一旦像我的情况那样定义了该变量,我就会使用它将其他 XML 数据拉入生成的输出中。 concat 命令强制数据一起给出一个文件名。

              所以当我想从外部 XML 文件中获取信息时,我使用“$DOCUMENTNAME”来引用外部数据。

              【讨论】:

                【解决方案7】:

                通过添加static parameters,现在可以在 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>
                

                【讨论】:

                  【解决方案8】:

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

                  您必须创建一个在运行时通过应用程序传递的系统属性,就像我使用 Java 一样,所以我使用命令创建了“调试”属性来运行我的应用程序:

                  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 中,还添加了对局部变量的支持。所以这可以很容易地使用局部变量来完成。

                  谢谢, 快乐编码

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2023-03-16
                    • 2015-01-14
                    • 1970-01-01
                    • 2020-08-19
                    • 2012-05-04
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多