2010-08-28 13 views
7

Sto cercando di ordinare un gruppo di record in un file XML. Il trucco è che ho bisogno di ordinare usando diversi elementi per diversi nodi. Per fare un esempio semplice, voglio fare questo: dato un file XMLxsl: ordina un file XML utilizzando più elementi

<?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> 

voglio convertirlo in

Andy 
Joe Bumpkin 
Pumpkin 
Jim Zulkin 

Cioè, una persona può essere elencato da qualsiasi sottoinsieme del primo nome, cognome e nick. La chiave di ordinamento è il cognome se è presente, altrimenti è il nickname se è presente e altrimenti il ​​nome.

Sono in difficoltà qui dall'utilizzo di variabili come xsl: le chiavi di ordinamento sono apparently not allowed.

mio colpo migliore attuale è quella di avere una trasformazione in due fasi: Aggiungi un tag speciale ad ogni record utilizzando questo foglio di stile

<?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> 

E poi ordinare l'XML risultante

<?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> 

Mentre questa trasformazione in due passaggi funziona, mi chiedo se c'è un modo più elegante di farlo in una volta sola?

risposta

5

È possibile utilizzare la funzione concat XPath:

<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

Grazie mille! –

+0

+1 per la risposta 'fn: concat'. Ho modificato perché l'output della tua risposta non è l'output desiderato –

+1

@ Zhenya: Prendi nota che ciò dipende in gran parte dalle lettere maiuscole. Se non puoi, a causa del tuo input, dovresti usare la concatenazione del separatore di spazi. –

1

Puoi provare se questo modello fornisce il risultato previsto? Per il tuo semplice esempio dà la risposta corretta ma potrebbero esserci casi d'angolo che non funzioneranno. Lo spazio di normalizzazione è usato qui per rimuovere gli spazi iniziali e finali se uno degli elementi è mancante.

<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

E 'più semplice (e penso che ancora corretta) se non utilizza gli spazi nella chiave di ordinamento a tutti. – svick

+0

Grazie mille per la funzione concat()! BTW, Si scopre che la variante di svick, dove gli spazi non sono usati affatto, funziona pure. –

+0

@svick Questo è vero in questo caso, ma in casi simili che coinvolgono elementi contenenti numeri interi, l'eliminazione di un delimitatore comporterebbe un ordinamento errato. – vallismortis

Problemi correlati