2009-02-03 20 views
5

I tiene un XML simple con dos niveles (cabecera y línea) de etiquetas tales como:XSLT Agrupación

<?xml version="1.0"?> 
<Header> 
    <line>Line 1</line> 
    <line>Line 2</line> 
    <line>Line 3</line> 
    <line>Line 4</line> 
    <line>Line 5</line> 
    <line>Line 6</line> 
    <line>Line 7</line> 
    <line>Line 8</line> 
    <line>Line 9</line> 
</Header> 

necesito para agrupar las líneas en conjuntos de X (X = 3, por ejemplo) líneas de modo que mi salida es el siguiente:

<?xml version="1.0"?> 
<Header> 
    <set> 
     <line>Line 1</line> 
     <line>Line 2</line> 
     <line>Line 3</line> 
    </set> 
    <set> 
     <line>Line 4</line> 
     <line>Line 5</line> 
     <line>Line 6</line> 
    </set> 
    <set> 
     <line>Line 7</line> 
     <line>Line 8</line> 
     <line>Line 9</line> 
    </set> 
</Header> 

¿Cómo se escribe un XSLT que puede hacer este tipo de transformación?

Gracias!

O

+0

@otavio Usted probablemente está utilizando un buggy, procesador XSLT no conforme, o, más probablemente, cambió el documento XML de origen, o cambiado el código. - Dimitre Novatchev (0 segundos) –

Respuesta

4

La siguiente transformación produce el resultado deseado:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:variable name="vN" select="3"/> 

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:apply-templates 
      select="line[position() mod $vN = 1]"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="line"> 
    <set> 
     <xsl:apply-templates mode="copy" select= 
     ". 
     | 
     following-sibling::line[position() &lt; $vN]"/> 
    </set> 
    </xsl:template> 

    <xsl:template match="line" mode="copy"> 
     <xsl:copy-of select="."/> 
    </xsl:template> 
</xsl:stylesheet> 

cuando se aplica sobre el documento XML proporcionado:

<Header> 
    <line>Line 1</line> 
    <line>Line 2</line> 
    <line>Line 3</line> 
    <line>Line 4</line> 
    <line>Line 5</line> 
    <line>Line 6</line> 
    <line>Line 7</line> 
    <line>Line 8</line> 
    <line>Line 9</line> 
</Header> 

el resultado es:

<Header> 
    <set> 
    <line>Line 1</line> 
    <line>Line 2</line> 
    <line>Line 3</line> 
    </set> 
    <set> 
    <line>Line 4</line> 
    <line>Line 5</line> 
    <line>Line 6</line> 
    </set> 
    <set> 
    <line>Line 7</line> 
    <line>Line 8</line> 
    <line>Line 9</line> 
    </set> 
</Header> 

hacer la nota lo siguiente:

  1. El uso de la XPath mod operador para averiguar el primer elemento de line en cada grupo de vN elementos.

  2. El uso de modos de, con el fin de ser capaz de procesar diferentes line elementos por diferentes plantillas

+0

funcionó después de cambiar a Otavio

+0

¡Tienes razón, Dimitre! Acabo de descubrir mi error e implementé el mismo concepto en mi problema real y parece funcionar bien. ¡Gracias de nuevo! (Eliminaré el comentario anterior) – Otavio

1

En general, en XSLT si desea crear una jerarquía de una lista se puede hacer uso de los hermanos-que preceden y siguen-hermanos palabras clave. Esto es fácil si hay una entrada de marcador entre conjuntos.

Como usted no tiene un marcador como tal en este caso me imagino que una solución podría implicar la palabra clave siguiente-hermanos y el operador mod. El mod que proporciona la división entre conjuntos.

Yo no lo he probado, pero que sería mi primera apertura.

xslt es normalmente un buen lugar para comenzar con la comprensión de las diferentes palabras clave.

0

Eso debería ser posible. Tiene la salida deseada:

<?xml version='1.0' encoding='UTF-8'?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       version="1.0"> 

    <xsl:template match="/Header"> 
    <Header> 
     <xsl:for-each select="line"> 
     <xsl:if test="not(number()=0) and position() mod 3 = 0"> 
      <set> 
      <xsl:variable name="pos" select="position()"/> 
      <line><xsl:value-of select="../line[position()=($pos -2)]"/></line> 
      <line><xsl:value-of select="../line[position()=($pos -1)]"/></line> 
      <line><xsl:value-of select="text()"/></line> 
      </set> 
     </xsl:if> 

     </xsl:for-each> 
    </Header> 
    </xsl:template> 

</xsl:stylesheet> 

(El pos-$ 1, $ pos-2 que no es muy bonito)

1

http://www.xml.com/pub/a/2003/11/05/tr.html muestra una forma un poco menos feo de hacer esto mediante XSLT 2.0.El elemento clave es ésta:

<xsl:for-each-group select="*" group-ending-with="*[position() mod 3 = 0]">