XSLT1.0とXSLT2.0の両方で、XPath式を動的に評価することはできません。
したがって、何をしようとしても$pParentPath
、望ましい結果は得られません。
回避策として、2つの異なるパラメーターを渡すことができます。pchildName
そしてpgrandchildName
次のようなものを使用します。
*[name()=$pchildName]/*[name()=$pgrandchildName]
XSLT 1.0では、変数またはパラメーターの参照は一致パターンで禁止されています。XSLT2.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
(親が異なり、深さが異なる2つの要素を含み、その子には特殊な処理が必要です)。
<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>
最後に、変換をさらに変更して、異なる名前の親の子を処理できるようにすることができます-say innerElement
and 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ドキュメントに適用される場合、必要な方法で処理される子には、次の2つの名前(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>
説明:
これは基本的に前の質問と同じロジックです。
2パス処理。
IDルールをオーバーライドします。
テンプレートとテンプレートの一致パターンの適切な使用。
名前の優先順位で要素を並べ替えます。