我正在调整来自第三方的XSLT,它将任意数量的XML转换为单个HTML文档。这是一个非常复杂的脚本,它将在未来进行修改,所以我正在尝试进行最小程度的调整,以使其能够满足我们的需求。
以下是XSLT的精简版(包含要点):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:param name="files" select="document('files.xml')//File"/>
<xsl:param name="root" select="document($files)"/>
<xsl:template match="/">
<xsl:for-each select="$root/RootNode">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="RootNode">
<xsl:for-each select="//Node">
<xsl:text>Node: </xsl:text><xsl:value-of select="."/><xsl:text>, </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
现在files.xml
包含要包含的文件的所有网址列表(在本例中为本地文件file1.xml
和file2.xml
)。因为我们想要从内存而不是从磁盘读取XML,并且因为XSLT的调用仅允许单个XML源,所以我将这两个文件组合在一个XML文档中。以下是两个文件的组合(实际情况可能更多)
<?xml version="1.0" encoding="UTF-8"?>
<TempNode>
<RootNode>
<Node>1</Node>
<Node>2</Node>
</RootNode>
<RootNode>
<Node>3</Node>
<Node>4</Node>
</RootNode>
</TempNode>
第一个RootNode
最初位于file1.xml
,第二个位于file2.xml
。
由于实际XSLT的复杂性,我认为我最好的尝试是尝试改变$root
- param。我尝试了以下内容:
<xsl:param name="root" select="/TempNode"/>
问题是这个。对于<xsl:param name="root" select="document($files)"/>
,"//Node"
中的XPath表达式<xsl:for-each select="//Node">
会独立地从file1.xml
和file2.xml
中选择节点,即生成以下(所需)列表:
节点:1,节点:2,节点:3,节点:4,
但是,当我将两个文件的内容合并为一个XML并解析它(并使用建议的$root
- 定义)时,表达式"//Node"
将选择所有节点的子节点TempNode
。 (换句话说,如上所示,由于与外部<xsl:for-each select="$root/RootNode">
循环的组合,所需的列表会产生两次。)
(如注释a中所示),在this page中,document()
显然会更改根节点,可能会解释此行为。)
我的问题变成了:
如何使用组合的XML作为源而不是通过document()的多源重新定义$root
,以便列表只生成一次,而不触及XSLT的其余部分?< / strong>就像使用$root
- 函数定义document()
一样,param中没有公共根节点。是否可以使用两个“单独”节点树定义一个参数?
顺便说一下:我已经尝试过定义这样的文件
<xsl:param name="root">
<xsl:for-each select="/TempNode/*">
<xsl:document>
<xsl:copy-of select="."/>
</xsl:document>
</xsl:for-each>
</xsl:param>
认为它可以解决问题,但"//Node"
表达式仍然会获取所有节点。 <xsl:template match="RootNode">
- 模板中的上下文节点实际上是输入文档中的某个位置而不是param吗? (老实说,当谈到上下文节点时,我很困惑。)
提前致谢!
答案 0 :(得分:1)
(更新了更多)
好的,有些问题变得清晰了。首先,为了确保我理解,您实际上并没有将$files
和$root
的参数传递给XSLT处理器调用,对吧? (它们可能是变量而不是参数?)
现在主要问题...在XPath中,当您评估以“/”开头的表达式(包括“//”)时,忽略上下文节点 [大多数] 。因此,当你有
<xsl:template match="RootNode">
<xsl:for-each select="//Node">
忽略匹配的RootNode。也许你想要
<xsl:template match="RootNode">
<xsl:for-each select=".//Node">
其中for-each将选择作为匹配的RootNode的后代的Node元素? 这将解决您生成所需节点列表两次的问题。
我在上面插入 [大多数] ,因为我记得“绝对位置路径”从“包含上下文节点的文档的根节点”开始。因此上下文节点确实会影响用于“// Node”的文档。也许那就是你一直想要的?我想我很难接受。
(旁注:如评论中所述 a)在这个页面中,文件()显然是 或许改变根节点 解释这种行为。)
从副作用的角度来看,绝对位置路径[“/ ...”] 然后是相对位置 path ...选择那组节点 将由亲戚选择 相对于根的位置路径 包含该文档的节点 上下文节点。
document()
实际上并没有改变任何东西;相反,它返回一组节点(通常)包含不同于主源文档的文档。像xsl:apply-templates
和xsl:for-each
这样的XSLT指令为其模板体范围内的上下文节点建立了新值。因此,如果您将xsl:apply-templates
和xsl:for-each
与select =“document(...)/ ...”一起使用,那些指令范围内的上下文节点将属于外部文档,因此任何使用“/ ...”作为XPath将从该外部文档开始。
再次更新
如何使用。重新定义$ root 将XML组合为源而不是 多源通过document(),所以 列表只生成一次, 没有接触剩余的 XSLT?
正如@Alej暗示的那样,鉴于上述限制,这是不可能的。如果你在“$ root / RootNode”循环的每次迭代中选择“// Node”,那么为了使每次迭代不选择与其他迭代相同的节点,每个值“$ root / RootNode”必须位于不同的文档中。由于您使用的是组合XML源,而不是多源,因此无法实现。
但是如果你不坚持你的<xsl:for-each select="//...">
XPath表达式不能改变,那就变得非常简单了。 :-)只需输入“。”在“//”。
就像使用document() - 函数定义$ root一样,没有共同的根节点 在参数中。
param的值是节点集。集合中的所有节点可能包含在同一文档中,也可能不包含在内,具体取决于document()的第一个参数是节点集还是单个节点。
是否可以使用两个“单独”节点树定义一个参数?
我相信“分开”,你的意思是“属于不同的文件”?是的,但我不认为你可以在XSLT 1.0中做到这一点,除非你首先选择属于不同文档的节点。
你提到过尝试
<xsl:param name="root">
<xsl:for-each select="/TempNode/*">
<xsl:document>
<xsl:copy-of select="."/>
</xsl:document>
</xsl:for-each>
</xsl:param>
但是,<xsl:document>
未在XSLT 1.0中定义,并且样式表显示版本=“1.0”。你有XSLT 2.0吗?如果是这样,请告诉我们,我们可以选择此选项。说实话,<xsl:document>
对我来说并不熟悉。但我很高兴和你一起学习。
答案 1 :(得分:0)
您只能应用所需的节点:
输入:
<?xml version="1.0" encoding="UTF-8"?>
<TempNode>
<RootNode>
<Node>1</Node>
<Node>2</Node>
</RootNode>
<RootNode>
<Node>3</Node>
<Node>4</Node>
</RootNode>
</TempNode>
<?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:output method="html" indent="yes"/>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="TempNode/RootNode"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RootNode">
<xsl:value-of select="concat('RootNode-', generate-id(.), ' ')"/>
<xsl:apply-templates select="Node"/>
</xsl:template>
<xsl:template match="Node">
<xsl:value-of select="concat('Node', ., ' ')"/>
</xsl:template>
</xsl:stylesheet>
输出:
RootNode-N65540 Node1 Node2 RootNode-N65549 Node3 Node4