XSLT:需要替代document() - 用于多源处理的函数

时间:2010-11-10 12:10:13

标签: xslt

我正在调整来自第三方的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.xmlfile2.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.xmlfile2.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吗? (老实说,当谈到上下文节点时,我很困惑。)

提前致谢!

2 个答案:

答案 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)在这个页面中,文件()显然是   或许改变根节点   解释这种行为。)

more precisely

  

绝对位置路径[“/ ...”]   然后是相对位置   path ...选择那组节点   将由亲戚选择   相对于根的位置路径   包含该文档的节点   上下文节点

从副作用的角度来看,

document()实际上并没有改变任何东西;相反,它返回一组节点(通常)包含不同于主源文档的文档。像xsl:apply-templatesxsl:for-each这样的XSLT指令为其模板体范围内的上下文节点建立了新值。因此,如果您将xsl:apply-templatesxsl: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(.), '&#10;')"/>
        <xsl:apply-templates select="Node"/>
    </xsl:template>

    <xsl:template match="Node">
        <xsl:value-of select="concat('Node', ., '&#10;')"/>
    </xsl:template>
</xsl:stylesheet>

输出:

RootNode-N65540
Node1
Node2
RootNode-N65549
Node3
Node4