In XSLT 1.0 verwenden Sie am besten einen zweistufigen Ansatz.
- tokenize die Eingabe von kommagetrennte in separate Elemente
- Gruppe an den separaten Elementen
Schritt # 1 tokenizes die Eingabe:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/root">
<countries>
<xsl:apply-templates select="entry" />
</countries>
</xsl:template>
<xsl:template match="entry">
<xsl:call-template name="tokenize">
<xsl:with-param name="input" select="countries" />
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="input" />
<xsl:variable name="list" select="concat($input, ',')" />
<xsl:variable name="head" select="substring-before($list, ',') " />
<xsl:variable name="tail" select="substring-after($list, ',') " />
<xsl:if test="normalize-space($head) != ''">
<country>
<xsl:value-of select="normalize-space($head)" />
</country>
<xsl:call-template name="tokenize">
<xsl:with-param name="input" select="$tail" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
produziert:
<countries>
<country>USA</country>
<country>Australia</country>
<country>Canada</country>
<country>USA</country>
<country>Australia</country>
<country>Australia</country>
<country>Belgium</country>
<country>Croatia</country>
</countries>
Schritt # 2 gilt Muench-Gruppierung an den Vermittler Ergebnis:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="text" />
<xsl:key name="kCountry" match="country" use="." />
<xsl:template match="/countries">
<xsl:apply-templates select="country">
<xsl:sort select="count(key('kCountry', .))" data-type="number" order="descending" />
<xsl:sort select="." data-type="text" order="ascending" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="country">
<xsl:if test="generate-id() = generate-id(key('kCountry', .)[1])">
<xsl:value-of select="." />
<xsl:text>	</xsl:text>
<xsl:value-of select="count(key('kCountry', .))" />
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
erzeugt das gewünschte Ergebnis (Formatierung wird für den Leser als Übung):
Australia 3
USA 2
Belgium 1
Canada 1
Croatia 1
Der Prozess kann in getan werden eine einzelne Transformation, mit Hilfe der Erweiterungsfunktion node-set()
. Sie würden jedoch die Möglichkeit verlieren, einen XSL-Schlüssel zu verwenden, was zu einer langsameren Leistung für große Eingaben führen kann. YMMV.
Die notwendige Modifikation von Schritt # 1 (unter Verwendung der MSXSL Erweiterungen anderer Hersteller unterscheiden sich in der Namensraum-Deklaration, die Übertragbarkeit von Dieser Ansatz reduziert) sein würde:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
>
<xsl:template match="/root">
<!-- store the list of <country>s as a result-tree-fragment -->
<xsl:variable name="countries">
<xsl:apply-templates select="entry" />
</xsl:variable>
<!-- convert the result-tree-fragment to a usable node-set -->
<xsl:variable name="country" select="msxsl:node-set($countries)/country" />
<!-- iteration, sorting and grouping in one step -->
<xsl:for-each select="$country">
<xsl:sort select="count($country[. = current()])" data-type="number" order="descending" />
<xsl:sort select="." data-type="text" order="ascending" />
<xsl:if test="generate-id() = generate-id($country[. = current()][1])">
<xsl:value-of select="." />
<xsl:text>	</xsl:text>
<xsl:value-of select="count($country[. = current()])" />
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- ... the remainder of the stylesheet #1 is unchanged ... -->
</xsl:stylesheet>
Mit diesem Ansatz ein separater Schritt # 2 wird unnötig. Das Ergebnis ist das gleiche wie oben. Bei kleinen Eingaben ist der Leistungsunterschied nicht bemerkbar.
Ich habe die Antwort mit einigen weiteren Punkten aktualisiert, wie Sie das beheben können. –
Welcher Prozessor ist das und vor allem XSLT 1.0 oder XSLT 2.0? –