2009-05-16 10 views
9

Tengo un documento XML dinámico que representa una estructura de árbol de categorías, pero lo hace utilizando atributos de ruta separada en orden arbitrario - como esto:Creación de una estructura de árbol anidado de un camino en XSLT

<data>  
     <record ID="24" Name="category 1\sub category 1"/> 
     <record ID="26" Name="category 1"/>  
     <record ID="25" Name="category 1\sub category 1\sub category 2"/>  
     <record ID="27" Name="category 1\sub category 1\sub category 3"/>  
     ... 
    </data> 

necesito llegar a una solución que 'normaliza' el XML para que se transforma en algo como esto:

<data>  
     <record ID="26" Name="category 1">  
     <record ID="24" Name="sub category 1">  
      <record ID="25" Name="sub category 2"/> 
      <record ID="27" Name="sub category 3"/>  
     </record> 
     </record> 
     ... 
    </data> 

Básicamente me preguntaba si esto es algo XSLT podría ser capaz de hacer frente, y cómo, en vez que tener g para hacerlo programáticamente.

+0

@myso: "sub categoría 3 "está en el mismo nivel que la" subcategoría 2 "en su XML de entrada. No se puede anidar de la manera que se muestra en su XML de salida. – Tomalak

+0

Sí, mi error. Lo he editado – mysomic

Respuesta

18

Claro, no hay problema:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 

    <xsl:output indent="yes" /> 

    <xsl:template match="/data"> 
    <!-- copy the document element --> 
    <xsl:copy> 
     <!-- That's where we start: all "record" nodes that have no "\". --> 
     <xsl:apply-templates mode="recurse" select="/data/record[ 
     not(contains(@Name, '\')) 
     ]" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="record" mode="recurse"> 
    <xsl:param name="starting-path" select="''" /> 

    <!-- The record node and its ID attribute can be copied. --> 
    <xsl:copy> 
     <xsl:copy-of select="@ID" /> 

     <!-- Create the new "name" attribute. --> 
     <xsl:attribute name="Name"> 
     <xsl:value-of select="substring-after(@Name, $starting-path)" /> 
     </xsl:attribute> 

     <!-- Append a backslash to the current path. --> 
     <xsl:variable name="current-path" select="concat(@Name, '\')" /> 

     <!-- Select all "record" nodes that are one level deeper... --> 
     <xsl:variable name="children" select="/data/record[ 
     starts-with(@Name, $current-path) 
     and 
     not(contains(substring-after(@Name, $current-path), '\')) 
     ]" /> 

     <!-- ...and apply this template to them. --> 
     <xsl:apply-templates mode="recurse" select="$children"> 
     <xsl:with-param name="starting-path" select="$current-path" /> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

salida en mi sistema:

<data> 
    <record ID="26" Name="category 1"> 
    <record ID="24" Name="sub category 1"> 
     <record ID="25" Name="sub category 2"></record> 
     <record ID="27" Name="sub category 3"></record> 
    </record> 
    </record> 
</data> 

Tenga en cuenta que toda la solución se basa en la suposición de que todos los caminos son canónicos y no contienen barras invertidas de cola .

También tenga en cuenta que los elementos de "registro" no coincidentes/huérfanos no estarán en la salida (a menos que estén en el nivel raíz, por supuesto).

Una cosa más: el modo de plantilla ("recurse") no es estrictamente necesario. Lo incluí porque la plantilla está haciendo algo bastante especial, y podría existir la posibilidad de que exista otra plantilla en su solución que coincida con los nodos "de registro". En este caso, esta solución se puede colocar sin romper nada más. Para una solución independiente, los modos de plantilla se pueden eliminar en cualquier momento.

Ah, y la última cosa: Si desea que el documento resultado de ordenar por nombre, incluya una <xsl:sort> element con los <xsl:apply-templates> (ambas ocurrencias), así:

<xsl:apply-templates select="..."> 
    <xsl:sort select="@Name" data-type="text" order="ascending" /> 
</xsl:apply-templates> 
+0

Funciona perfectamente. ¡Qué sorprendente respuesta, muchas gracias! – mysomic

+0

De nada. :-) – Tomalak

+6

¡Hombre, solo rock! – Cerebrus

Cuestiones relacionadas