2010-08-28 5 views
7

Ich versuche, eine Menge Datensätze in einer XML-Datei zu sortieren. Der Trick ist, dass ich verschiedene Elemente für verschiedene Knoten verwenden muss. Um ein einfaches Beispiel zu geben, ich möchte, dies zu tun: Da eine XML-Dateixsl: Eine XML-Datei mit mehreren Elementen sortieren

<?xml version="1.0" encoding="utf-8" ?> 

<buddies> 

<person> 
<nick>Jim</nick> 
<last>Zulkin</last> 
</person> 

<person> 
<first>Joe</first> 
<last>Bumpkin</last> 
</person> 

<person> 
<nick>Pumpkin</nick> 
</person> 

<person> 
<nick>Andy</nick> 
</person> 

</buddies> 

ich konvertieren möchte es

Andy 
Joe Bumpkin 
Pumpkin 
Jim Zulkin 

Das heißt, eine Person kann durch jede Teilmenge der ersten aufgeführt werden Name, Nachname und ein Nickname. Der Sortierschlüssel ist der Nachname, wenn er vorhanden ist, ansonsten ist es der Spitzname, wenn er vorhanden ist, und andernfalls der Vorname.

Ich habe Schwierigkeiten hier seit der Verwendung von Variablen als xsl: Sortierschlüssel ist apparently not allowed.

Mein aktueller bester Schuss ist eine zweistufiger Transformation haben: einen speziellen Tag zu jedem Datensatz hinzufügen mit dieser Sheet

<?xml version="1.0" encoding="ISO-8859-1"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:output omit-xml-declaration="no" indent="yes"/> 

<!-- *** convert each person record into a person2 record w/ the sorting key *** --> 
<xsl:template match="/buddies"> 
    <buddies> 
    <xsl:for-each select="person"> 
    <person2> 
     <xsl:copy-of select="*"/> 
     <!-- add the sort-by tag --> 
     <sort-by> 
     <xsl:choose> 
      <xsl:when test="last"> <xsl:value-of select="last"/> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:choose> 
        <xsl:when test="nick"> <xsl:value-of select="nick"/> </xsl:when> 
        <xsl:otherwise> <xsl:value-of select="first"/> </xsl:otherwise> 
       </xsl:choose> 
      </xsl:otherwise> 
     </xsl:choose> 
    </sort-by> 
    </person2> 
</xsl:for-each> 
</buddies> 

Und dann sortieren Sie die resultierenden XML-

<?xml version="1.0" encoding="ISO-8859-1"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:template match="/buddies"> 
    <xsl:apply-templates> 
     <xsl:sort select="sort-by"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="person2"> 
    <xsl:value-of select="first"/> 
    <xsl:value-of select="nick"/> 
    <xsl:value-of select="last"/><xsl:text> 
</xsl:text> 
</xsl:template> 

Während diese zweistufige Transformation funktioniert, frage ich mich, ob es eleganter ist, es in einem einzigen Schritt zu tun?

Antwort

5

können Sie verwenden, um die concat XPath-Funktion:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:template match="/buddies"> 
     <xsl:apply-templates> 
      <xsl:sort select="concat(last,nick,first)"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="person"> 
     <xsl:value-of select="concat(normalize-space(concat(first, 
                  ' ', 
                  nick, 
                  ' ', 
                  last)), 
            '&#xA;')"/> 
    </xsl:template> 
</xsl:stylesheet> 
+0

Danke eine Zillion! –

+0

+1 für 'fn: concat' Antwort. Ich habe bearbeitet, weil Ihre Antwort-Ausgabe nicht die gewünschte Ausgabe ist –

+1

@Zhenya: Beachten Sie, dass dies stark in Großschreibung beruht. Wenn dies aufgrund Ihrer Eingabe nicht möglich ist, sollten Sie die Raumtrennungsverkettung verwenden. –

1

Können Sie versuchen, ob diese Vorlage das erwartete Ergebnis liefert? Für Ihr einfaches Beispiel gibt es die richtige Antwort, aber es gibt Fälle, die nicht funktionieren. Normalize-Space wird hier verwendet, um führende und nachfolgende Leerzeichen zu entfernen, wenn eines der Elemente fehlt.

<xsl:template match="/buddies"> 
    <xsl:for-each select="person"> 
     <xsl:sort select="normalize-space(concat(last, ' ', nick, ' ', first))"/> 

     <xsl:if test="first"> 
      <xsl:value-of select="first" /> 
      <xsl:text> </xsl:text> 
     </xsl:if> 

     <xsl:if test="nick"> 
      <xsl:value-of select="nick" /> 
      <xsl:text> </xsl:text> 
     </xsl:if> 

     <xsl:value-of select="last" /> 
     <xsl:text>&#10;</xsl:text> 
    </xsl:for-each> 
</xsl:template> 
+0

Es ist einfacher (und ich denke immer noch korrekt), wenn Sie tun, verwendet keine Leerzeichen in dem Sortierschlüssel an alle. – svick

+0

Vielen Dank für die Funktion concat()! BTW, Es stellt sich heraus, dass auch die Variante von svick funktioniert, bei der keine Leerzeichen verwendet werden. –

+0

@svick Das stimmt in diesem Fall, aber in ähnlichen Fällen, in denen Elemente mit Ganzzahlen verwendet werden, würde das Fehlen eines Begrenzers zu einer falschen Reihenfolge führen. – vallismortis

Verwandte Themen