この一般的なXSLT2.0変換:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="xs my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<t>
<xsl:sequence select="my:grouping(*)"/>
</t>
</xsl:template>
<xsl:function name="my:grouping" as="node()*">
<xsl:param name="pElems" as="element()*"/>
<xsl:if test="$pElems">
<xsl:for-each-group select="$pElems" group-by="my:signature(.)">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:sequence select="my:grouping(current-group()/*)"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:if>
</xsl:function>
<xsl:function name="my:signature" as="xs:string">
<xsl:param name="pElem" as="element()"/>
<xsl:variable name="vsignAttribs" as="xs:string*">
<xsl:for-each select="$pElem/@*">
<xsl:sort select="name()"/>
<xsl:value-of select="concat(name(), '=', .,'|')"/>
</xsl:for-each>
</xsl:variable>
<xsl:sequence select=
"concat(name($pElem), '|', string-join($vsignAttribs, ''))"/>
</xsl:function>
</xsl:stylesheet>
提供されたXMLに適用した場合(単一の最上位要素にラップされて整形式のXMLドキュメントになります):
<t>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
</Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="30" Number="3"/>
<RatingByNumber Code="X" Rating="39" Number="4"/>
</Rating>
</Ratings>
</TopLevel>
</t>
必要な正しい結果を生成します:
<t>
<TopLevel CodeTL="Something">
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
<RatingByNumber Code="X" Rating="30" Number="3"/>
<RatingByNumber Code="X" Rating="39" Number="4"/>
</Rating>
</Ratings>
</TopLevel>
</t>
説明:
実行されたグループ化は関数に実装され、my:grouping()
再帰的です。
最上位の要素はそのレベルで単一であり、それ自体の浅いコピー以外のグループ化は必要ありません。次に、この浅いコピーの本体内で、下位レベルのグループ化が関数によって実行されますmy:grouping()
。
この関数my:grouping()
には、すぐ上のレベルのグループ内のすべての要素のすべての子要素である単一の引数があります。現在のレベルのすべてのグループを返します。
関数に引数として渡される要素のシーケンスは、それらの署名に基づいてグループ化されます。つまり、要素の名前と、その属性および対応する値のすべての名前と値のペアの連結であり、これらは適切な区切り文字を使用して区切られます。要素のシグネチャは、関数によって生成されますmy:signature()
。
II。一般的なXSLT1.0ソリューション:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:my="my:my" exclude-result-prefixes="my ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="/*"/>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>
<xsl:template match="/">
<xsl:apply-templates select="$vPass1/*" mode="pass2"/>
</xsl:template>
<xsl:template match="/*" mode="pass2">
<xsl:copy>
<xsl:call-template name="my:grouping">
<xsl:with-param name="pElems" select="*"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="my:grouping">
<xsl:param name="pElems" select="/.."/>
<xsl:if test="$pElems">
<xsl:for-each select="$pElems">
<xsl:variable name="vPos" select="position()"/>
<xsl:if test=
"not(current()/@my:sign
= $pElems[not(position() >= $vPos)]/@my:sign
)">
<xsl:element name="{name()}">
<xsl:copy-of select="namespace::*[not(. = 'my:my')]"/>
<xsl:copy-of select="@*[not(name()='my:sign')]"/>
<xsl:call-template name="my:grouping">
<xsl:with-param name="pElems" select=
"$pElems[@my:sign = current()/@my:sign]/*"/>
</xsl:call-template>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/*">
<xsl:variable name="vSignature">
<xsl:call-template name="signature"/>
</xsl:variable>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:attribute name="my:sign">
<xsl:value-of select="$vSignature"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template name="signature">
<xsl:variable name="vsignAttribs">
<xsl:for-each select="@*">
<xsl:sort select="name()"/>
<xsl:value-of select="concat(name(), '=', .,'|')"/>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select=
"concat(name(), '|', $vsignAttribs)"/>
</xsl:template>
</xsl:stylesheet>
この変換が同じXMLドキュメント(上記)に適用されると、同じ正しい結果が生成されます。
<t>
<TopLevel>
<Ratings>
<Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
</Rating>
<Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
<RatingByNumber Code="X" Rating="10" Number="1"/>
<RatingByNumber Code="X" Rating="19" Number="2"/>
<RatingByNumber Code="X" Rating="30" Number="3"/>
<RatingByNumber Code="X" Rating="39" Number="4"/>
</Rating>
</Ratings>
</TopLevel>
</t>
説明:
これは2パス変換です。
すべての要素の最初のパスで署名が計算され、それが新しい属性の値になりますmy:sign
。
XSLT 2.0ソリューションの場合と同じ、再帰的なグループ化アルゴリズムが使用されます。