XSLT conditional include of external file
我想在XSLT中执行条件包含,但是xsl:include是顶级元素。您只能在模板内部使用xsl:if或xsl:choose。是否存在允许有条件地包含外部文件的任何形式的hack或变通方法?我尝试使用document()函数,但是无法加载我的外部文件(可能是因为它不符合使它"有效"的某些规则集)。
我的外部xml文件是一堆xslt代码片段。根据外部XSLT文件中变量的值,外部文件中的相应代码应"复制/粘贴"到位(例如C或PHP中的条件包含)。
我的主要XSLT文件的流应按以下方式进行:
1 2 | $configurationMode if ( $configurationMode = Standard ) { xsl:include="standard.xml" } else { xsl:include="alt.xml" } |
很显然,我不能像上面那样简单地做到这一点,因此为什么我要问是否有黑客或变通办法。
这不能用XSLT 1.0完成,可以在XSLT 2.0中使用
存在非xslt方式来实现
一种这样的方法是将XSLT样式表加载为XmlDocument,并使用可用的DOM方法访问和修改属性,以将
尝试反转结构:如果您有两个专用模块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。
据我所知,当xml解析器正在解析和编译样式表时,就会发生"包含"。这意味着在处理包含之前不会进行逻辑或表达式求值,因此无法将其作为条件条件。您需要使条件行为发生在样式表之外。
看看这个http://www.dpawson.co.uk/xsl/sect2/N4760.html#d6065e100
Mike Kay的这一评论特别有帮助:
This has been raised a number of times. On a previous thread we came
to the conclusion that the user was trying to write a general-purpose
stylesheet G and then specialize it by conditionally including a
special-purpose stylesheet A or B. The way to meet this requirement is
to have A and B include G, not the other way around, and then you
conditionally select A or B as the principal stylesheet when starting
the transformation.
不确定这是否适用于您的情况,但是无论如何我都会把它扔在那里。
过去我做过类似的事情,但是我没有做条件包含,而是根据xsl:when的计算结果在两个文件之一中调用一个模板。如果您不想使用模板,请忽略此答案。
例如,假设"父" xslt文件称为root.xslt,两个有条件使用的文件是child1.xslt和child2.xslt。假设我想对称为status的节点的值运行条件逻辑。如果值为" CURRENT",我想在child1.xslt中调用名为isCurrent的模板,否则在child2.xslt中调用名为isNotCurrent的模板。为了简洁起见,在每种情况下,我只是将模板传递给根节点和所有子节点。
它看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?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> |
希望这至少会有所帮助。
我发现有一种方法可以使用XSLT 2.0中的use-when功能有条件地包含文件。
您必须创建一个在运行时通过应用程序传递的系统属性,就像我使用Java一样,因此我已经使用命令创建了" Debug"属性来运行我的应用程序:
1 | java -DDebug=yes myAppPath |
然后在XSLT中使用此属性来包含条件文件。例如
1 2 | <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中,还添加了对局部变量的支持。因此,可以使用局部变量轻松地做到这一点。
谢谢,
快乐编码
通过添加静态参数,现在可以在XSLT 3.0中实现。可以在
现在我们可以使用默认值
1 2 3 | <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)
1 2 3 | <doc> <foo/> </doc> |
主XSLT 3.0(test_main.xsl)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <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)
1 2 3 4 5 6 7 | <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)
1 2 3 4 5 6 7 | <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> |
命令行(将
1 | java -cp"saxon9he.jar" net.sf.saxon.Transform -s:"test.xml" -xsl:"test_main.xsl" inc2="true" |
输出
1 2 3 | <doc> <foo>INCLUDE FILE 2!!!</foo> </doc> |
1 2 | <xsl:variable name="CONDITIONAL" select="ELEMENT/CONDITION"/> <xsl:variable name="DOCUMENTNAME" select="document(concat($CONDITIONAL,'-DOCUMENT.xml'))/ELEMENT"/> |
此示例是我有条件使用外部文件的方式。我在XSL用作变量的基础XML中有一个标志,以帮助指定要拉入模板的其他文件的名称。在我定义了该变量之后,我将其用于将其他XML数据提取到生成的输出中。 concat命令将数据强制在一起以给出文件名。
因此,当我想要外部XML文件中的信息时,我使用'$ DOCUMENTNAME'来引用外部数据。
除了已经说过的以外,一种可能的解决方案是使补充文件成为纯的,提供内容的XML文件(而不是XSLT文件)。这样,您可以将它们包含在XPath的
然后,您可以根据已加载的XML文档的内容来更改转换的行为;但是,您不能在随附的文档中提供可执行的XSLT片段。
这是否取决于解决方案取决于您的用例-如果其他文档对转换的控制流程有很大影响,则您不想用纯XML定义它们,因为您基本上会重新定义XML。实现类似XSLT的功能。但是,当您的文档用作某种配置文件时,您可能需要考虑为它们提供纯XML。