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>
考虑以下 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
:
<?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>