2010-08-28 15 views
7

Estoy tratando de ordenar un grupo de registros en un archivo XML. El truco es que necesito ordenar usando diferentes elementos para diferentes nodos. Para dar un ejemplo más simple, yo quiero hacer esto: dado un archivo XMLxsl: ordena un archivo XML usando múltiples elementos

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

quiero convertirlo en

Andy 
Joe Bumpkin 
Pumpkin 
Jim Zulkin 

Es decir, una persona puede aparecer por cualquier subconjunto de la primera nombre, apellido y un nick. La clave de clasificación es el apellido si está presente; de ​​lo contrario, es el alias si está presente y un nombre diferente.

Tengo dificultades aquí ya que el uso de variables como xsl: claves de clasificación es apparently not allowed.

Mi actual mejor tiro es tener una transformación de dos pasos: Agregar una etiqueta especial para cada registro usando esta hoja de estilo

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

Y a continuación, ordenar el XML resultante

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

Si bien esta transformación de dos pasos funciona, me pregunto si hay una forma más elegante de hacerlo de una sola vez.

Respuesta

5

Usted puede utilizar la función XPath concat:

<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

¡Gracias un trillón! –

+0

+1 para la respuesta 'fn: concat'. He editado porque su salida de respuesta no es la salida deseada –

+1

@Zhenya: tenga en cuenta que esto se basa en gran medida en las mayúsculas. Si no puede debido a su entrada, debe usar la concatenación del separador de espacio. –

1

¿Puedes probar si esta plantilla da el resultado esperado? Para su ejemplo simple, da la respuesta correcta, pero puede haber casos de esquina que no funcionen. Normalize-space se utiliza aquí para eliminar espacios iniciales y finales si falta uno de los elementos.

<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 más sencillo (y creo que sigue siendo correcta) si no utiliza espacios en la clave de ordenación en absoluto. – svick

+0

¡Muchas gracias por la función concat()! BTW, Resulta que la variante de svick, donde los espacios no se usan en absoluto, también funciona. –

+0

@svick Esto es cierto en este caso, pero en casos similares que involucran elementos que contienen enteros, omitir un delimitador llevaría a un orden incorrecto. – vallismortis

Cuestiones relacionadas