2011-01-24 19 views
8

Me gustaría saber cómo mover los nodos hasta un nivel usando XSLT si se cumplen ciertas condiciones. Para dar un ejemplo echar un vistazo a la siguiente fuente XML:XSLT: Mueva el nodo un nivel hacia arriba

<Settings> 
    <String [...]> 
    <Boolean [...]/> 
    </String> 
</Settings> 

Ese es el XML que tengo como situación de partida. Para ser claros, los nombres de nodo "Configuración", "Cadena", "Booleano" son nodos especiales que definimos.

El problema es que los nodos "booleanos" no están permitidos dentro de los nodos "String". Es por eso que tengo que mover esos nodos "booleanos" en el nivel superior. El XML deseado se vería así:

<Settings> 
    <String [...]></String> 
    <Boolean [...]/> 
</Settings> 

el XSLT también tiene que trabajar con cada nodo de cadena que tiene un nodo de Boole hermanos, independientemente de la posición en el XML-árbol.

Hasta ahora he aprendido que tienes que copiar primero la totalidad de su XML usando una "regla de identidad" y luego aplicar algunas reglas especiales para las transformaciones deseadas:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   
    xmlns:fo="http://www.w3.org/1999/XSL/Format" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:fn="http://www.w3.org/2005/xpath-functions"> 

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

    <!-- special rules ... --> 

</xsl:stylesheet> 

Lo estoy luchando con es la rule para mover todos los nodos "Boolean" que son hermanos de los nodos "String" de un nivel arriba. ¿¡¿Cómo puedo hacer eso?!?

+0

Buena pregunta, +1. Consulte mi respuesta para ver una variación del patrón de diseño "anular la regla de identidad": proporcioné una anulación ligeramente más precisa. :) –

Respuesta

6

Mi interpretación de la prescripción da la solución como

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

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

<xsl:template match="String"> 
    <xsl:copy> 
     <xsl:apply-templates select="child::node()[not(self::Boolean)]"/> 
    </xsl:copy> 
    <xsl:apply-templates select="Boolean"/> 
</xsl:template> 

</xsl:stylesheet> 
+1

Gracias Michael. Tu respuesta parece funcionar bien.Sólo se propondrá un cambio menor: convierte Con este cambio, los atributos de los nodos String se copian también. Muchas gracias. – Jens

+0

+1 Respuesta correcta. –

3

intente lo siguiente:

<!-- copy all --> 
<xsl:template match="*"> 
    <xsl:copy> 
     <xsl:copy-of select="@*" /> 
     <xsl:apply-templates /> 
    </xsl:copy> 
</xsl:template> 

<!-- ignore booleans-inside-strings --> 
<xsl:template match="String/Boolean" /> 

<!-- place the booleans-inside-strings into the settings element --> 
<xsl:template match="Settings"> 
    <xsl:copy> 
     <xsl:copy-of select="//String/Boolean" /> 
     <xsl:apply-templates /> 
    </xsl:copy> 
</xsl:template> 
+1

¿Hay alguna razón por la cual no 'match =" @ * | node() "' y 'select =" @ * | node() "' en su plantilla "copiar todo"? Sólo curiosidad ... – Filburt

+0

Gracias por tu respuesta. Eso funciona bien para mi ejemplo. Pero no funcionará siempre, ya que siempre copia el nodo booleano en el elemento de configuración. La regla debería copiar el elemento Boolean un nivel arriba. Piense en Eso se debe transformar a: Jens

+0

También, por qué la puesta en '' // // Cadena en '/ boolean'? –

3

Esta solución es bastante similar a la de Michae @-Kay. Sin embargo, la anulación de la regla de identidad es un poco más preciso - sólo String elementos que realmente tienen un hijo Boolean se emparejan:

<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="String[Boolean]"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()[not(self::Boolean)]|@*"/> 
    </xsl:copy> 
    <xsl:apply-templates select="Boolean"/> 
</xsl:template> 
</xsl:stylesheet> 

cuando se aplica esta transformación con los del siguiente documento XML:

<Settings> 
    <String> 
     <Boolean /> 
    </String> 
    <String/> 
</Settings> 

el resultado deseado, adecuados, se produce:

<Settings> 
    <String/> 
    <Boolean/> 
    <String/> 
</Settings> 
+0

¿podría también echar un vistazo a mi respuesta? No puedo encontrar un error que merezca la pena. No me importa la votación negativa, solo quiero ver si me perdí un error. – Flack

+0

@Flack: creo que su solución está bien. Solo necesita aplicar plantillas a los atributos de 'Setings'; de lo contrario, se perderán. He votado a favor su respuesta, pero por favor, agregue esta solución recomendada también. –

+0

Fue una omisión consciente, debido a la muestra proporcionada por OP. Aunque elegí eliminar mi respuesta ya que hay dos que están mejor diseñados. Voto a cambio de curso. Estaba esperando la reaparición de los votos disponibles. – Flack

Cuestiones relacionadas