Select xslt 中的并行节点

Select a parallel node in xslt

考虑以下 xml:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <base>
     <a>
            <b>
                <c>Text 1</c>
            </b>
        </a>
    </base>
    <base>
        <a>
            <b>
                <c>Text 2</c>
            </b>
        </a>
    </base>
</root>

和这个 xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates select="root/base[1]" />
    </xsl:template>

    <xsl:template match="base//*">
        <xsl:if test="text()">
            <xsl:value-of select="text()" />
            <!-- i need Text 2 here -->
        </xsl:if>            
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="text()" />
</xsl:stylesheet>

原来的 xml 有更多的嵌套,我不知道确切的结构。但是有一个结构相同的并行节点。如果我的模板在 //root/base[1]/a/b/c 我想引用 //root/base[2]/a/b/c

但是我只知道我在//root/base[1]下面的某个节点中,并且在//root/base[2]中有相同的节点。

有没有可能实现这个目标?

试试下面的代码。仅当每个基本元素都有具有唯一元素名称的子元素时,此代码才有效。

<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">
   <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates select="root/base[1]" />
    </xsl:template>

    <xsl:template match="base//*">
        <xsl:if test="text()">
            <xsl:variable name="name" select="name()"/>
            <xsl:value-of select="text()" />
            <xsl:value-of select="ancestor::base/following-sibling::base[1]//*[name() = $name]" />
       </xsl:if>            
       <xsl:apply-templates />
   </xsl:template>

<xsl:template match="text()" />

但是您可以使用来自 exslt 扩展的 dyn:evaluate 或 saxon:eval

使用 saxon:evaluate 扩展 (http://saxon.sourceforge.net/saxon7.9/extensions.html#evaluate) 的解决方案

如果您使用的是 xslt 1.0 处理器 (http://exslt.org/dyn/index.html),您也可以使用 dyn:evaluate:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
xmlns:saxon="http://saxon.sf.net/"
version="1.0">
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates select="root/base[1]" />
    </xsl:template>

    <xsl:template match="base//*">
        <xsl:if test="text()">
            <xsl:variable name="path">
                <xsl:call-template name="constructPath">
                    <xsl:with-param name="node" select="."/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="rewritedPath" select="concat('/root/base[2]', substring($path, 11))"/>

            <xsl:value-of select="text()" />
            <xsl:value-of select="saxon:evaluate($rewritedPath)" />
        </xsl:if>            
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template name="constructPath">
        <xsl:param name="node"/>

        <xsl:choose>
            <xsl:when test="$node/parent::node()/name()">
                <xsl:call-template name="constructPath">
                    <xsl:with-param name="node" select="$node/parent::node()"/>
                </xsl:call-template>
                <xsl:value-of select="concat('/', $node/name())"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat('/', $node/name())"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="text()" />
</xsl:stylesheet>

这里我们使用递归模板"constructPath"创建字符串并在evaluate()函数中处理它。这将 select 与并行分支中的确切节点。希望这会有所帮助。

此样式表不需要扩展,也不对唯一名称做任何假设;无聊的部分是每次调用 xsl:apply-templates:

时都传递正确的 "twin node"
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates select="root/base[1]">
            <xsl:with-param name="twin" select="root/base[2]"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="*">
        <xsl:param name="twin"/>
        <xsl:if test="text()">
            <xsl:value-of select="text()" />
            <xsl:value-of select="$twin/text()"/>
        </xsl:if>            
        <xsl:for-each select="*">
            <xsl:variable name="pos" select="position()"/>
            <xsl:apply-templates select=".">
                <xsl:with-param name="twin" select="$twin/*[$pos]"/>
            </xsl:apply-templates>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="text()" />
</xsl:stylesheet>