2012-09-25 32 views
5

entrada XMLXSLT fusión/concatenación de los valores de hermanos nodos del mismo nombre en solo nodo

<catalog> 
    <product id="1"> 
     <name>abc</name> 
     <category>aaa</category> 
     <category>bbb</category> 
     <category>ccc</category> 
    </product> 
    <product id="2"> 
     <name>cde</name> 
     <category>aaa</category> 
     <category>bbb</category> 
    </product> 
</catalog> 

esperado XML de salida

<products> 
    <product> 
     <id>1</id> 
     <name>abc</name> 
     <category>aaa,bbb,ccc</category> 
    </product> 
    <product> 
     <id>2</id> 
     <name>cde</name> 
     <category>aaa,bbb</category> 
    </product> 
</products> 

XSLT para la transformación

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:template match="/catalog"> 
     <products> 
      <xsl:for-each select="product"> 
       <product> 
        <id><xsl:value-of select="@id"/></id> 
        <name><xsl:value-of select="name"/></name> 
        <category><xsl:value-of select="category" /></category> 
       </product> 
      </xsl:for-each> 
     </products> 
    </xsl:template> 
</xsl:stylesheet> 

Actual XML de salida: (

<products> 
    <product> 
     <id>1</id> 
     <name>abc</name> 
     <category>aaa</category> 
    </product> 
    <product> 
     <id>2</id> 
     <name>cde</name> 
     <category>aaa</category> 
    </product> 
</products> 

Código necesario en el bucle a través de todos los nodos hermanos con el nombre 'categoría' debajo de cada 'producto' y fusión/concatenación en un solo nodo separado por una coma. El número de 'categoría' varía para cada producto y, por lo tanto, se desconoce el recuento.

Respuesta

8

El uso de este útil unirse a la llamada-plantilla definida here, esto es tan simple como:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:template match="/catalog"> 
     <products> 
      <xsl:for-each select="product"> 
       <product> 
        <id> 
         <xsl:value-of select="@id"/> 
        </id> 
        <name> 
         <xsl:value-of select="name"/> 
        </name> 
        <category> 
         <xsl:call-template name="join"> 
          <xsl:with-param name="list" select="category" /> 
          <xsl:with-param name="separator" select="','" /> 
         </xsl:call-template> 
        </category> 
       </product> 
      </xsl:for-each> 
     </products> 
    </xsl:template> 

    <xsl:template name="join"> 
     <xsl:param name="list" /> 
     <xsl:param name="separator"/> 

     <xsl:for-each select="$list"> 
      <xsl:value-of select="." /> 
      <xsl:if test="position() != last()"> 
       <xsl:value-of select="$separator" /> 
      </xsl:if> 
     </xsl:for-each> 
    </xsl:template> 

</xsl:stylesheet> 

Salida:

<products> 
    <product> 
    <id>1</id> 
    <name>abc</name> 
    <category>aaa,bbb,ccc</category> 
    </product> 
    <product> 
    <id>2</id> 
    <name>cde</name> 
    <category>aaa,bbb</category> 
    </product> 
</products> 
6

En XSLT 2.0 sólo tiene que hacer un pequeño cambio en el código de :

<category><xsl:value-of select="category" separator=","/></category> 

Tenga en cuenta que si necesita una solución de XSLT 1.0 es una buena idea decirlo. Algunas personas en algunos entornos están estancadas en 1.0, pero mucha gente no lo está.

+0

Ah sí, es 1.0, y la versión de la hoja de estilo xslt lo dice. – user1677271

+1

El número de versión en la hoja de estilo no nos dice nada acerca de las capacidades del procesador XSLT que está utilizando o la capacidad del proyecto para pasar a un procesador más actualizado. –

3

Aquí hay otra solución XSLT 1.0.

Cuando esto XSLT:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
version="1.0"> 
    <xsl:output omit-xml-declaration="no" indent="yes" /> 
    <xsl:strip-space elements="*" /> 

    <xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="product"> 
    <xsl:copy> 
     <xsl:apply-templates select="*[not(self::category)]" /> 
     <category> 
     <xsl:apply-templates select="category/text()" /> 
     </category> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="category/text()"> 
    <xsl:if test="position() &gt; 1">,</xsl:if> 
    <xsl:value-of select="."/> 
    </xsl:template> 
</xsl:stylesheet> 

... se aplica a XML de la OP Original:

<catalog> 
    <product id="1"> 
    <name>abc</name> 
    <category>aaa</category> 
    <category>bbb</category> 
    <category>ccc</category> 
    </product> 
    <product id="2"> 
    <name>cde</name> 
    <category>aaa</category> 
    <category>bbb</category> 
    </product> 
</catalog> 

... el resultado deseado se produce:

<?xml version="1.0"?> 
<catalog> 
    <product> 
    <name>abc</name> 
    <category>aaa,bbb,ccc</category> 
    </product> 
    <product> 
    <name>cde</name> 
    <category>aaa,bbb</category> 
    </product> 
</catalog> 

Explicación:

  • La primera plantilla - el Identity Template - coincide con todos los nodos y los atributos y las copia en el documento resultado tal cual.
  • La segunda plantilla anula la Plantilla de identidad creando un nuevo elemento <category> y procesando los elementos secundarios de texto de cada elemento <category> en la ubicación actual del documento.
  • La plantilla final genera los valores de texto y las comas según sea necesario.
+0

No he intentado pero aprecio las diferentes soluciones ABach :) – user1677271

Cuestiones relacionadas