I. 単純な XSLT 1.0 変換:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kEntryByChildren" match="entry" use="."/>
<xsl:template match=
"entry[not(generate-id() = generate-id(key('kEntryByChildren', .)[1]))]"/>
<xsl:template match="entry">
<tr>
<xsl:apply-templates/>
<td><xsl:value-of select="count(key('kEntryByChildren', .))"/></td>
</tr>
</xsl:template>
<xsl:template match="entry/*">
<td><xsl:value-of select="."/></td>
</xsl:template>
<xsl:template match="/*">
<table>
<xsl:apply-templates/>
</table>
</xsl:template>
</xsl:stylesheet>
提供された XML に適用した場合(整形式の XML ドキュメントを取得するために、フラグメントは単一の最上位要素にラップされます):
<t>
<entry>
<attribute1>A</attribute1>
<attribute2>B</attribute2>
</entry>
<entry>
<attribute1>A</attribute1>
<attribute2>B</attribute2>
</entry>
<entry>
<attribute1>C</attribute1>
<attribute2>D</attribute2>
</entry>
<entry>
<attribute1>E</attribute1>
<attribute2>F</attribute2>
</entry>
</t>
必要な正しい結果が生成されます。
<table>
<tr>
<td>A</td>
<td>B</td>
<td>2</td>
</tr>
<tr>
<td>C</td>
<td>D</td>
<td>1</td>
</tr>
<tr>
<td>E</td>
<td>F</td>
<td>1</td>
</tr>
</table>
このトリッキーな XML ドキュメントに適用するとentry
(子の値の単純な連結を使用すると、最初の 3 つの要素が「同じ」であると誤って結論付けてしまいます)。
<t>
<entry>
<attribute1>AB</attribute1>
<attribute2>C</attribute2>
</entry>
<entry>
<attribute1>A</attribute1>
<attribute2>BC</attribute2>
</entry>
<entry>
<attribute1>A</attribute1>
<attribute2>BC</attribute2>
</entry>
<entry>
<attribute1>C</attribute1>
<attribute2>D</attribute2>
</entry>
<entry>
<attribute1>E</attribute1>
<attribute2>F</attribute2>
</entry>
</t>
正しい結果が生成されます。
<table>
<tr>
<td>AB</td>
<td>C</td>
<td>1</td>
</tr>
<tr>
<td>A</td>
<td>BC</td>
<td>2</td>
</tr>
<tr>
<td>C</td>
<td>D</td>
<td>1</td>
</tr>
<tr>
<td>E</td>
<td>F</td>
<td>1</td>
</tr>
</table>
説明:
Muenchian グループ化法の適切な使用。
注意してください:
このソリューションは、要素の子の名前と数に依存しないため、entry
2 つ以上の子がある場合、または事前に名前が不明なさまざまな数の子がある場合に適用できます。
ここでは、同じ子が同じ値を持つ場合にのみ、すべての子の文字列値の連結が同じであると想定しています。
Ⅱ.完全な XSLT 1.0 ソリューション:
上記の仮定 2. が保証されない場合、これは可能な XSLT 1.0 ソリューションの 1 つです。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="entry">
<xsl:variable name="vChildrenFp">
<xsl:for-each select="*">
<xsl:value-of select="concat(., '+')"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="vPrecedingSame">
<xsl:for-each select="preceding-sibling::entry">
<xsl:variable name="vthisFP">
<xsl:for-each select="*">
<xsl:value-of select="concat(., '+')"/>
</xsl:for-each>
</xsl:variable>
<xsl:if test="$vthisFP = $vChildrenFp">1</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="not(string($vPrecedingSame))">
<xsl:variable name="vFollowingSame">
<xsl:for-each select="following-sibling::entry">
<xsl:variable name="vthisFP">
<xsl:for-each select="*">
<xsl:value-of select="concat(., '+')"/>
</xsl:for-each>
</xsl:variable>
<xsl:if test="$vthisFP = $vChildrenFp">1</xsl:if>
</xsl:for-each>
</xsl:variable>
<tr>
<xsl:apply-templates/>
<td><xsl:value-of select="string-length($vFollowingSame)+1"/></td>
</tr>
</xsl:if>
</xsl:template>
<xsl:template match="entry/*">
<td><xsl:value-of select="."/></td>
</xsl:template>
<xsl:template match="/*">
<table>
<xsl:apply-templates/>
</table>
</xsl:template>
</xsl:stylesheet>
同じ XML ドキュメント (上記) に適用すると、同じ正しい結果が生成されます。
<table>
<tr>
<td>A</td>
<td>B</td>
<td>2</td>
</tr>
<tr>
<td>C</td>
<td>D</td>
<td>1</td>
</tr>
<tr>
<td>E</td>
<td>F</td>
<td>1</td>
</tr>
</table>
説明:
要素ごとentry
に、その子の「フィンガープリント」(FP) を生成しentry
、前の兄弟entry
要素のいずれにも同じ子のフィンガープリントがない場合は、この要素を処理します。
「同じ」entry
要素のカウントは同様の方法で行われます。同じ子の FP 値を持つ後続の兄弟entry
要素については、1 文字 ('1') を出力します。合計カウントは、そのように生成された文字列 (「1」) の文字列長に 1 を加えたものです。
III. XSLT 2.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="my xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pExoticString" select="'+'"/>
<xsl:template match="/*">
<table>
<xsl:for-each-group select="entry" group-by="my:fingerprint(.)">
<tr>
<xsl:apply-templates/>
<td><xsl:value-of select="count(current-group())"/></td>
</tr>
</xsl:for-each-group>
</table>
</xsl:template>
<xsl:template match="entry/*">
<td><xsl:value-of select="."/></td>
</xsl:template>
<xsl:function name="my:fingerprint" as="xs:string">
<xsl:param name="pParent" as="element()"/>
<xsl:sequence select="string-join($pParent/*, $pExoticString)"/>
</xsl:function>
</xsl:stylesheet>
このシンプルなソリューションは、複雑なケースを簡単に処理します。最後の XML ドキュメントに適用すると、必要な正しい結果が生成されます。
<table>
<tr>
<td>AB</td>
<td>C</td>
<td>1</td>
</tr>
<tr>
<td>A</td>
<td>BC</td>
<td>2</td>
</tr>
<tr>
<td>C</td>
<td>D</td>
<td>1</td>
</tr>
<tr>
<td>E</td>
<td>F</td>
<td>1</td>
</tr>
</table>
説明:
xsl:for-each-group
、xsl:function
、current-group()
およびの適切な使用string-join()
。