2010-06-01 28 views
7

Necesito una solución XSL para reemplazar nodos XML con nodos nuevos.usando XSL para reemplazar nodos XML con nodos nuevos

Digamos que tiene la siguiente estructura XML existente:

<root> 
    <criteria> 
     <criterion>AAA</criterion> 
    </criteria> 
</root> 

Y quiero reemplazar el nodo uno de los criterios con:

<criterion>BBB</criterion> 
<criterion>CCC</criterion> 
<criterion>DDD</criterion> 

Así que el resultado final XML es:

<root> 
    <criteria> 
     <criterion>BBB</criterion> 
     <criterion>CCC</criterion> 
     <criterion>DDD</criterion> 
    </criteria> 
</root> 

He intentado usar subserie-antes y subserie-después para copiar la primera mitad de la estructura, luego ju Copie la segunda mitad (para completar mis nuevos nodos entre las dos mitades) pero parece que las funciones de subcadena solo reconocen el texto entre las etiquetas de los nodos, y no las etiquetas como yo quiero. :(:(

Cualesquiera otras soluciones?

+0

Buena pregunta (+1). Vea mi respuesta para una solución correcta y corta. Tenga en cuenta que la solución actualmente aceptada tiene un defecto lógico (simplemente funciona con su documento XML, pero generalmente producirá resultados incorrectos con otros documentos XML). –

Respuesta

21

XSL no puede reemplazar nada. Lo mejor que puede hacer es copiar las partes que desea conservar, a continuación, dar salida a las partes que quiera cambiar el lugar de las piezas usted no desea mantener


Ejemplo:.

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" 
> 
    <xsl:output method="xml" indent="yes"/> 

    <!-- This is an identity template - it copies everything 
     that doesn't match another template --> 
    <xsl:template match="@* | node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <!-- This is the "other template". It says to use your BBB-DDD elements 
     instead of the AAA element --> 
    <xsl:template match="criterion[.='AAA']"> 
    <xsl:element name="criterion"> 
     <xsl:text>BBB</xsl:text> 
    </xsl:element> 
    <xsl:element name="criterion"> 
     <xsl:text>CCC</xsl:text> 
    </xsl:element> 
    <xsl:element name="criterion"> 
     <xsl:text>DDD</xsl:text> 
    </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

La coincidencia de la plantilla @* | node() coincide con cualquier atributo o cualquier otro tipo de nodo. El truco es que las coincidencias de plantillas tienen prioridades. Puedes pensar en la regla como "el partido más específico gana". Cualquier cosa va a ser más específico que "cualquier atributo u otro nodo". Esto hace que la "identidad" coincida con una prioridad muy baja.

Cuando coincide con, simplemente copia cualquier nodo que encuentre dentro del atributo o nodo coincidente.

Cualquier otra plantilla que tenga tendrá una mayor prioridad. Lo que sea que coincidan, es el código dentro de la plantilla más específica que tendrá efecto. Por ejemplo, si simplemente elimina todo lo que está dentro de la plantilla criterion[.='AAA'], encontrará que ha copiado exactamente su entrada, excepto el elemento "AAA".

+0

@iheartgreek eche un vistazo muy de cerca a las plantillas que John ha configurado. Las plantillas que coinciden con los elementos XML son la clave para usar XSLT de manera efectiva. –

+0

Gracias por proporcionar un ejemplo de código ... ¡esto es exactamente lo que estaba tratando de lograr! Si bien esta solución funciona para mí, realmente quiero entenderlo también. Hice cosas similares con la segunda plantilla (match = "criterio ['AAA']") pero la parte clave que no sabía (o me daba) era la plantilla que copiaba todo lo que no coincide con otra plantilla. Entonces ... ¿Puedes explicar cómo la coincidencia de XPath = "@ * | node()" logra esto? Soy nuevo en XPath y XSL, así que todavía no sé los detalles. – developer

+0

¡Gracias! ¡eso tiene mucho más sentido ahora que sé que las plantillas tienen prioridades! : D – developer

0

Bajo la regla general de-más-que-unidireccional a piel-a-cat

<?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"/> 
    <xsl:template match="/"> 
     <xsl:apply-templates /> 
    </xsl:template> 
    <!-- 
     when you capture a node with the text 'AAA' 
      emit the BBB, CCC, DDD nodes 
    --> 
    <xsl:template match="criterion[text() = 'AAA']"> 
     <xsl:element name="criterion"> 
      <xsl:text>BBB</xsl:text> 
     </xsl:element> 
     <xsl:element name="criterion"> 
      <xsl:text>CCC</xsl:text> 
     </xsl:element> 
     <xsl:element name="criterion"> 
      <xsl:text>DDD</xsl:text> 
     </xsl:element> 
    </xsl:template> 
    <!-- identity template --> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
     <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 
+0

¿Podría explicarme en qué se diferencia de mi solución? –

+0

@John: no hay diferencia, pero el tuyo tiene mucha más explicación. Creo que lo publiqué y/entonces/lo actualicé ... me pegaste al golpe :-) –

6

Aquí es una solución correcta, que es probablemente una de las más cortas:

<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="criterion[. = 'AAA']"> 
    <criterion>BBB</criterion> 
    <criterion>CCC</criterion> 
    <criterion>DDD</criterion> </xsl:template> 
</xsl:stylesheet> 

Cuando se aplica esta transformación en el documento XML proporcionado, el resultado deseado se produce:

<root> 
    <criteria> 
     <criterion>BBB</criterion> 
     <criterion>CCC</criterion> 
     <criterion>DDD</criterion> 
    </criteria> 
</root> 

hacer la nota:

  1. El uso de la plantilla de identidad .

  2. Cómo la plantilla de identidad es reemplazada por una plantilla específica - solo para un elemento criterion, cuyo valor de cadena es 'AAA'.

+0

"bastante impreciso"? Creo que ha señalado un solo error, que Mads Hansen no solo señaló, sino que también explicó y corrigió. –

+0

@ John-Saunders: Me complace que el error haya sido corregido. Trataré de cancelar mi downvote. Espero que esto te ayude la próxima vez a prestar más atención. Mi principio es rechazar todo lo que contenga errores (al menos obvios). Lamentablemente, no puedo darme el tiempo para corregir los errores en las respuestas de los demás. –

+2

@Dimitre: su actitud no es útil. Intenté responder la pregunta. Cometí un error. La prueba en mi código no corregido se evalúa como 'true()'. Como resultado, la hoja de estilos pareció funcionar. No hay razón para su comentario de "prestar más atención". Si no puede molestarse en participar en la comunidad aquí (que implica la ayuda para corregir las malas respuestas), entonces podría considerar una diferente, en la que su contribución inútil de un voto a la baja silencioso pasará inadvertido. –

Cuestiones relacionadas