この変換:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kSameNameAdj" match="*"
use="concat(generate-id(..),
'+',
generate-id(preceding-sibling::*[not(name()=name(current()))][1])
)"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="node()"/>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vrtfPass1)" mode="compress"/>
</xsl:template>
<xsl:template name="explode" match=
"*[contains(name(),'_')
and not(substring(name(),1,1)='_')
and not(substring(name(), string-length(name()))='_')
]">
<xsl:param name="pName" select="name()"/>
<xsl:param name="pText" select="text()"/>
<xsl:choose>
<xsl:when test="not($pName)">
<xsl:value-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{substring-before(concat($pName, '_'), '_')}">
<xsl:call-template name="explode">
<xsl:with-param name="pName" select="substring-after($pName, '_')"/>
<xsl:with-param name="pText" select="$pText"/>
</xsl:call-template>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/" name="fold" mode="compress">
<xsl:param name="pDoc" select="/"/>
<xsl:choose>
<xsl:when test="not($pDoc//*[name()=name(following-sibling::*[1])])">
<xsl:copy-of select="$pDoc"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vrtfThisPass">
<xsl:apply-templates select="$pDoc/*" mode="compress"/>
</xsl:variable>
<xsl:call-template name="fold">
<xsl:with-param name="pDoc" select="ext:node-set($vrtfThisPass)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|@*" mode="compress">
<xsl:copy>
<xsl:apply-templates select="@*|node()[1]" mode="compress"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]" mode="compress"/>
</xsl:template>
<xsl:template match="*[name()=name(following-sibling::*[1])]" mode="compress">
<xsl:element name="{name()}">
<xsl:apply-templates mode="compress" select=
"key('kSameNameAdj',
concat(generate-id(..),
'+',generate-id(preceding-sibling::*)
)
)/node()"/>
</xsl:element>
<xsl:apply-templates mode="compress" select=
"key('kSameNameAdj',
concat(generate-id(..),
'+',generate-id(preceding-sibling::*)
)
)
[last()]/following-sibling::node()[1]
"/>
</xsl:template>
</xsl:stylesheet>
次の XML ドキュメントに適用した場合(提供されたもので、より困難になるように拡張されています):
<root>
<x>This is:</x>
<a_b_c>hello</a_b_c>
<a_b_c_d>my</a_b_c_d>
<a_b_c1>wonderful</a_b_c1>
<a_b_c>world</a_b_c>
<a_b>!</a_b>
<y>The End</y>
</root>
必要な正しい結果が生成されます。
<root>
<x>This is:</x>
<a>
<b>
<c>hello<d>my</d>
</c>
<c1>wonderful</c1>
<c>world</c>!</b>
</a>
<y>The End</y>
</root>
説明:
.1. これはマルチパス変換です。最初のパスは、XML ドキュメントを次のように変換します。
<root>
<x>This is:</x>
<a>
<b>
<c>hello</c>
</b>
</a>
<a>
<b>
<c>
<d>my</d>
</c>
</b>
</a>
<a>
<b>
<c1>wonderful</c1>
</b>
</a>
<a>
<b>
<c>world</c>
</b>
</a>
<a>
<b>!</b>
</a>
<y>The End</y>
</root>
.2. 後続のパスでは、それぞれが同じ名前を持つ隣接する要素のグループをその名前を持つ単一の要素に圧縮します。次に、同じ名前を持つ複数の隣接する兄弟要素のグループがなくなるまで、結果が再帰的に処理されます。
.3. 最初のパスでは、アイデンティティ ルールを使用します。
.4. 次のパスでは、「圧縮」モードで高品質の IDテンプレートを使用します。