在 XSLT 1.0 和 XSLT 2.0 中,不可能动态地计算 XPath 表达式。
因此,您尝试执行的操作$pParentPath
不会产生预期的结果。
作为一种解决方法,您可以传递两个不同的参数:pchildName
并pgrandchildName
使用如下内容:
*[name()=$pchildName]/*[name()=$pgrandchildName]
在 XSLT 1.0 中,匹配模式中禁止变量或参数引用。在 XSLT 2.0 中是可以的。
这是转换,已更正以处理此特定 XML 文档:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pUncertainElName" select="'second'"/>
<xsl:param name="pOrderedNames" select="'|first|second|third|'"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="innerElement">
<xsl:variable name="vrtfFirstPass">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<xsl:apply-templates select=
"self::*[not(*[name() = $pUncertainElName])
or
*[name()=$pUncertainElName and @missing-cause]]"
mode="missing"/>
</xsl:copy>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
</xsl:template>
<xsl:template match="*[@missing-cause]"/>
<xsl:template match="*" mode="missing">
<xsl:element name="{$pUncertainElName}">
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</xsl:element>
</xsl:template>
<xsl:template match="innerElement" mode="pass2">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort data-type="number" select=
"string-length(substring-before($pOrderedNames,
concat('|', name(), '|')
)
)"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的 XML 文档时:
<doc>
<outerElement>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second missing-cause="bla" />
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
</outerElement>
</doc>
产生了想要的正确结果:
<doc>
<outerElement>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second>
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</second>
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
</outerElement>
</doc>
可以修改转换,以便它处理文档层次结构不同位置的不同元素的子元素:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pUncertainElName" select="'second'"/>
<xsl:param name="pOrderedNames" select="'|first|second|third|'"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfFirstPass">
<xsl:apply-templates select="node()|@*"/>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
</xsl:template>
<xsl:template match="innerElement">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<xsl:apply-templates select=
"self::*[not(*[name() = $pUncertainElName])
or
*[name()=$pUncertainElName and @missing-cause]]"
mode="missing"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@missing-cause]"/>
<xsl:template match="*" mode="missing">
<xsl:element name="{$pUncertainElName}">
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</xsl:element>
</xsl:template>
<xsl:template match="node()|@*" mode="pass2">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="pass2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="innerElement" mode="pass2">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort data-type="number" select=
"string-length(substring-before($pOrderedNames,
concat('|', name(), '|')
)
)"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
当此转换应用于以下 XML 文档(包含两个innerElement
元素 - 具有不同的父级和不同的深度 - 其子级需要专门处理)时:
<doc>
<outerElement>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second missing-cause="bla" />
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
<outerElement2>
<outerElement3>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
</outerElement3>
</outerElement2>
</outerElement>
</doc>
产生了想要的正确结果:
<doc>
<outerElement>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second>
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</second>
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
<outerElement2>
<outerElement3>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second>
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</second>
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
</outerElement3>
</outerElement2>
</outerElement>
</doc>
最后,我们可以进一步修改转换,以便它可以处理不同名字父母的孩子——比如innerElement
和someOtherInnerElement
:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pUncertainElName" select="'second'"/>
<xsl:param name="pOrderedNames" select="'|first|second|third|'"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfFirstPass">
<xsl:apply-templates select="node()|@*"/>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
</xsl:template>
<xsl:template match="innerElement | someOtherInnerElement">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<xsl:apply-templates select=
"self::*[not(*[name() = $pUncertainElName])
or
*[name()=$pUncertainElName and @missing-cause]]"
mode="missing"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@missing-cause]"/>
<xsl:template match="*" mode="missing">
<xsl:element name="{$pUncertainElName}">
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</xsl:element>
</xsl:template>
<xsl:template match="node()|@*" mode="pass2">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="pass2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="innerElement | someOtherInnerElement" mode="pass2">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort data-type="number" select=
"string-length(substring-before($pOrderedNames,
concat('|', name(), '|')
)
)"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
当此转换应用于以下 XML 文档时,其中要以所需方式处理的子级具有使用这两个名称 (innerElement
和someOtherInnerElement
) 命名的父级:
<doc>
<outerElement>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second missing-cause="bla" />
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
<outerElement2>
<outerElement3>
<someOtherInnerElement>
<first>
<textElement>Some Text</textElement>
</first>
<third>
<textElement>Some Text</textElement>
</third>
</someOtherInnerElement>
</outerElement3>
</outerElement2>
</outerElement>
</doc>
再次产生所需的正确结果:
<doc>
<outerElement>
<innerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second>
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</second>
<third>
<textElement>Some Text</textElement>
</third>
</innerElement>
<outerElement2>
<outerElement3>
<someOtherInnerElement>
<first>
<textElement>Some Text</textElement>
</first>
<second>
<CharacterString>INSERTED BY TEMPLATE</CharacterString>
</second>
<third>
<textElement>Some Text</textElement>
</third>
</someOtherInnerElement>
</outerElement3>
</outerElement2>
</outerElement>
</doc>
说明:
这与上一个问题的逻辑基本相同:
二次处理。
覆盖身份规则。
正确使用模板和模板匹配模式。
按名称的首选顺序对元素进行排序。