2011-05-16 6 views
5

así que quiero convertir el siguiente utilizando XSLEl uso de XSL para pasar de un nodo a otro

<doc> 
    <data id="priority" level="2" include="true"> 
     <name>Priority</name> 
    </data> 
    <data id="cost" level="1" leveltype="number"> 
     <name>Cost</name> 
    </data> 
    <data id="date" level="3" include="true"> 
     <name>Date</name> 
    </data> 
</doc> 

Para este

<doc> 
    <data id="priority"> 
     <name>Priority</name> 
    </data> 
    <data id="cost"> 
     <name>Cost</name> 
    </data> 
    <data id="date"> 
     <name>Date</name> 
    </data> 

    <!-- ordering matters, though if necessary I can reorder this manually via the DOM instead of XSL --> 
    <levels> 
     <level id="cost" include="false" type="number"/> 
     <level id="priority" include="true"/> 
     <level id="date" include="true"/> 
    </level> 
</doc> 

Básicamente quiero tomar los atributos de nivel y hacer que su propio cosa. Una gran ventaja sería si hubiera alguna manera de eliminar el número de nivel y usar el orden del nodo para representar eso.

+0

No puedo ver nada ... –

+0

Si puede agregar muestras de lo que tiene, así como muestras del resultado final, eso nos ayudaría a ayudarle. –

+0

Buena pregunta, +1. Consulte mi respuesta para obtener una solución completa, corta y basada en plantillas; esta podría ser la más corta, la más simple y la más fácil de ampliar de todas. –

Respuesta

0

Aquí está la solución:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:output indent="yes"/> 

    <!-- attribute suppression template --> 
    <xsl:template match="@*" priority="2"/> 

    <xsl:template match="/doc"> 
    <xsl:copy> 
     <xsl:apply-templates select="*" mode="data"/> 
     <levels> 
     <xsl:apply-templates select="*" mode="levels"> 
      <xsl:sort select="@level" data-type="number"/> 
     </xsl:apply-templates> 
     </levels> 
    </xsl:copy> 
    </xsl:template> 


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

    <xsl:template match="@*" mode="data"/><!-- suppress --> 
    <xsl:template match="@id" mode="data" priority="2"><!-- keep --> 
    <xsl:copy-of select="."/></xsl:template> 


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

    <xsl:template match="data" mode="levels"> 
    <level> 
     <xsl:apply-templates select="@*" mode="levels"/> 
    </level></xsl:template> 

    <xsl:template match="@level" mode="levels"/><!-- suppress --> 
    <xsl:template match="@leveltype" mode="levels"><!-- rename --> 
    <xsl:attribute name="type"><xsl:value-of select="."/> 
    </xsl:attribute></xsl:template> 

</xsl:stylesheet> 

que asumen que <level id="cost" include="false" type="number"/> en su resultado esperado es un artefacto de copiar/pegar como el atributo no se encuentra en level[@id="cost"] en la entrada.

0

puede ser una manera más simple:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:template match="/"> 
    <doc> 
     <xsl:for-each select="doc/data"> 
      <data> 
       <xsl:attribute name="id"> 
        <xsl:value-of select="@id"/> 
       </xsl:attribute> 
       <name><xsl:value-of select="name" /></name> 
      </data> 
     </xsl:for-each> 
     <levels> 
      <xsl:for-each select="doc/data"> 
       <xsl:sort select="@level" /> 
       <level> 
        <xsl:attribute name="id"> 
         <xsl:value-of select="@id"/> 
        </xsl:attribute> 
        <xsl:choose> 
         <xsl:when test="@include='true'"> 
          <xsl:attribute name="include">true</xsl:attribute> 
         </xsl:when> 
         <xsl:otherwise> 
          <xsl:attribute name="include">false</xsl:attribute> 
         </xsl:otherwise> 
        </xsl:choose> 
       </level> 
      </xsl:for-each> 
     </levels> 
    </doc> 
</xsl:template> 
</xsl:stylesheet> 
2

Sólo una variante:

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

<xsl:template match="doc"> 

<doc> 

    <!-- build and sort data nodes --> 
    <xsl:for-each select="data"> 
    <xsl:sort select="@id"/> 
    <data id="{@id}"> 
    <xsl:copy-of select="name"/> 
    </data> 
    </xsl:for-each> 

    <!-- build and sort levels --> 
    <levels> 
    <xsl:for-each select="data"> 
    <xsl:sort select="@id"/> 
     <level id="{@id}" include="{boolean(@include)}"> 
     <xsl:if test="@leveltype"> 
     <xsl:attribute name="type"> 
     <xsl:value-of select="@leveltype"/> 
     </xsl:attribute> 
     </xsl:if> 
     </level> 
    </xsl:for-each> 
    </levels> 

</doc> 

</xsl:template> 
</xsl:stylesheet> 
+0

Buena implementación. – Mic

3

Esta es una solución más corto y más simple utilizando sólo las plantillas (sin <xsl:for-each>):

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

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

<xsl:template match="/*"> 
    <doc> 
    <xsl:apply-templates select="*"/> 
    <levels> 
    <xsl:apply-templates select="data" mode="level"> 
    <xsl:sort select="@level" data-type="number"/> 
    </xsl:apply-templates> 
    </levels> 
    </doc> 
</xsl:template> 
<xsl:template match="data/@*[not(name()='id')]"/> 

<xsl:template match="data" mode="level"> 
    <level id="{@id}" include="{boolean(@include)}"> 
    <xsl:if test="@leveltype"> 
    <xsl:attribute name="type"><xsl:value-of select="@leveltype"/></xsl:attribute> 
    </xsl:if> 
    </level> 
</xsl:template> 
</xsl:stylesheet> 

Cuando se aplica o n el documento previsto XML:

<doc> 
    <data id="priority" level="2" include="true"> 
     <name>Priority</name> 
    </data> 
    <data id="cost" level="1" leveltype="number"> 
     <name>Cost</name> 
    </data> 
    <data id="date" level="3" include="true"> 
     <name>Date</name> 
    </data> 
</doc> 

el resultado deseado, correcta se produce:

<doc> 
    <data id="priority"> 
     <name>Priority</name> 
    </data> 
    <data id="cost"> 
     <name>Cost</name> 
    </data> 
    <data id="date"> 
     <name>Date</name> 
    </data> 
    <levels> 
     <level id="cost" include="false" type="number"/> 
     <level id="priority" include="true"/> 
     <level id="date" include="true"/> 
    </levels> 
</doc> 

Explicación:

  1. Uso y anulando la regla de identidad/plantilla .

  2. Utilizando mode="level" para generar la segunda parte del documento de resultados.

+0

+1. He vagado un tiempo por una solución sin 'xsl: for-each'. –

+1

@empo: Gracias: uso '' (casi) solo en el caso cuando es necesario cambiar el documento actual por otro para que la función 'key()' funcione con el segundo documento. Aparte de este caso de uso, no creo que haya ninguna otra situación en la que '' sea necesario. –

+0

suena simple! Pensaré en esto la próxima vez que voy a usar un bucle. –

Cuestiones relacionadas