2009-07-28 8 views
0

Lassen Sie uns sagen, dass ich die folgende XML-Struktur haben:berechnen Anzahl der Keywords in XSLT

<entry> 
    <countries>USA, Australia, Canada</countries> 
</entry> 
<entry> 
    <countries>USA, Australia</countries> 
</entry> 
<entry> 
    <countries>Australia, Belgium</countries> 
</entry> 
<entry> 
    <countries>Croatia</countries> 
</entry> 

ich Anzahl von Instanzen zählen möchte für jedes Land in dieser Einträge angezeigt. Ich kann nur clientseitige XSLT verwenden (kein benutzerdefinierter Servercode zulässig). Die Endergebnisse muss wie folgt aussehen:

 
Country | Count 
-----------|-------- 
Australia |  3 
USA  |  2 
Belgium |  1 
Canada  |  1 
Croatia |  1 

Als Mike deutete-out diese XML-Struktur verbessert werden könnte, aber es ist von 3rd-Party-System erzeugt wird, und ich kann es nicht ändern.

Ist es möglich, diese XSLT zu erreichen, und wenn ja, wie?

+0

Ich habe die Antwort mit einigen weiteren Punkten aktualisiert, wie Sie das beheben können. –

+0

Welcher Prozessor ist das und vor allem XSLT 1.0 oder XSLT 2.0? –

Antwort

0

Gibt es einen Grund, den Sie das Format nicht verwenden:

<entry> 
    <countries> 
    <country>USA</country> 
    <country>Australia</country> 
    <country>Canada</country> 
    </countries> 
</entry> 

Ihre aktuelle Art und Weise nicht wirklich übereinstimmen, wie XML-Daten gespeichert werden sollen.

Wie Sie gesagt haben, Sie können nicht das Datenformat ändern, um eine Kombination aus tokenize() und count() versuchen (vorausgesetzt, Sie XSLT2 Unterstützung haben, sonst glaube ich, du bist kein Glück).

+0

Ich weiß, und ich stimme zu, aber diese Ausgabe wird von 3rd-Party-System produziert und ich kann das nicht ändern. –

2

In XSLT 1.0 verwenden Sie am besten einen zweistufigen Ansatz.

  1. tokenize die Eingabe von kommagetrennte in separate Elemente
  2. 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>&#9;</xsl:text> 
     <xsl:value-of select="count(key('kCountry', .))" /> 
     <xsl:text>&#10;</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>&#9;</xsl:text> 
     <xsl:value-of select="count($country[. = current()])" /> 
     <xsl:text>&#10;</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.

0
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="xsl str msxsl" version="1.0"> 
    <xsl:import href="str.split.template.xsl"/> 
    <xsl:output indent="yes"/> 

    <xsl:template match="/"> 
    <xsl:variable name="countries"> 
     <xsl:call-template name="get-counties" /> 
    </xsl:variable> 

    <table> 
    <xsl:for-each select="msxsl:node-set($countries)/country[not(. = preceding::country)]"> 
     <xsl:variable name="name" select="./text()"/> 
     <tr> 
     <td> 
      <xsl:value-of select="$name" /> 
     </td> 
     <td> 
      <xsl:value-of select="count(msxsl:node-set($countries)/country[. = $name])" /> 
     </td> 
     </tr> 
    </xsl:for-each> 
    </table> 
    </xsl:template> 

    <xsl:template name="get-counties"> 
    <xsl:for-each select="//countries"> 
     <xsl:variable name="countries-raw"> 
     <xsl:call-template name="str:split"> 
      <xsl:with-param name="string" select="text()"/> 
      <xsl:with-param name="pattern" select="','" /> 
     </xsl:call-template> 
     </xsl:variable> 

     <xsl:for-each select="msxsl:node-set($countries-raw)/token"> 
     <country> 
      <xsl:value-of select="normalize-space(.)"/> 
     </country> 
     </xsl:for-each> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

str.split.template.xsl ist ein Teil des Moduls von str EXSLT (http://www.exslt.org/download.html).

+0

Ich denke, das Sortieren der Ausgabe war Teil der Aufgabe. – Tomalak

Verwandte Themen