2009-11-20 15 views
21

Me gustaría calcular la diferencia entre dos archivos o nodos XML usando XSL/XSLT. ¿Hay alguna hoja de estilo disponible o alguna forma simple de hacerlo?XML Diff: ¿Cómo generar XML diff usando XSLT?

+3

+1 Sólo por la ambición de que, por cierto qué versión de XSLT estabas buscando para que esto funcione en – AnthonyWJones

+0

El objetivo sería diff dos nodos en el mismo archivo XML, ¿es eso más factible? – Vincent

+0

Debe definir qué significa igual y diferente en términos XML. Como referencia, mire esta pregunta http://stackoverflow.com/questions/4546190/compare-two-xml-files-with-xslt –

Respuesta

6

¡Pregunta interesante! Una vez intenté hacer algo similar con dos fuentes XML, y mi experiencia fue que simplemente no hay forma.

Puede usar las funciones de XSL para incluir funciones creadas por el usuario y codificar algo realmente ingenioso. Pero realmente no puedo verlo.

Si tuviera que hacer algo como esto, procesaría los dos archivos XML en paralelo usando DOM4J, lo que me permite atravesar fácilmente el código programáticamente y detallar sub-consultas.

Intentar hacer esto en XSLT probará que eres un genio o te llevará a la locura.

2

XSLT está basado en datos, es decir, va a través del archivo XML fuente única de arriba hacia abajo buscando coincidencias de plantilla en la hoja de estilos XSL. Las plantillas realmente no saben dónde están en los datos, solo ejecutan su código cuando coinciden. Puede hacer referencia a otra fuente XML, pero el programa se ejecutará de acuerdo con el recorrido de la fuente original.

Así que cuando llegue al elemento n-ésimo hijo de <blarg>, por ejemplo, podría buscar el enésimo hijo de <blarg> en un segundo XML utilizando la función document(). Pero la utilidad de esto depende de la estructura de su XML y de las comparaciones que intenta hacer.

Este comportamiento es opuesto a la mayoría de los scripts tradicionales, que se ejecutan a través del código de programa de arriba a abajo, invocando el archivo de datos cuando se le indica. El último, el proceso de extracción, es lo que probablemente necesite para comparar dos fuentes XML. XSLT se descompondrá en comparación tan pronto como haya una diferencia.

1

Hay formas de hacerlo, pero no diría que es simple.

En el pasado he utilizado una utilidad de código abierto llamado diffmk, esto produce una salida XML con etiquetas adicionales que muestran lo que se ha añadido/eliminado ...

que tenía que escribir una hoja de estilo adicional a continuación, convertir este en un informe HTML más legible

Algunas herramientas de diff como XMLSpy Diff dog son buenas, pero costosas.

1

¡Esto no es un misterio! Aquí están los pasos generales:

  1. @carillonator tiene razón acerca de cómo XSLT procesa documentos. Para facilitarlo, combinamos las dos versiones de sus documentos en un único documento que puede usar para ejecutar su diferencia XSLT (puede hacerlo a través de la línea de comando con bash, o con el lenguaje de programación que esté utilizando, o incluso con otro Transformada XSLT [tubería]). Es sólo una encapsulación:

    <diff_container> 
        <version1> 
         ... first version here 
        </version1> 
        <version2> 
         ... second version here 
        </version2> 
    </diff_container> 
    
  2. continuación Corremos este documento a través de nuestro diff XSLT, el XSLT entonces tiene la tarea de simplemente recorrer el árbol y la comparación de nodos entre las dos versiones. Esto puede ir desde muy simple (¿Se cambió un elemento? ¿Movido? ¿Eliminado?) A semi complejo. Una buena comprensión de XPath lo hace bastante simple.

    Como dijimos antes, trabajas en un entorno diferente, por lo que estás limitado en comparación con herramientas como Diff Dog. Sin embargo, el beneficio de tener el algoritmo en XSLT también puede tener un valor real.

Hope this help. ¡Aclamaciones!

2

Si lo que quiere decir con diff es algo así como comprobar si existen elementos en un documento (o nodo) pero no en otro, puede utilizar la tecla XPath() con un tercer parámetro

<?xml version="1.0"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs ="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xsl xs"> 

<xsl:param name="doc2diff" required="yes"/> 
<!-- docB is root node of the "second" document --> 
<xsl:variable name="docB" select="document($doc2diff)"/> 
<!-- docA is the root node of the first document --> 
<xsl:variable name="docA" select="/"/> 
<xsl:output method="xml" encoding="UTF-8" indent="yes"/> 
<xsl:key name="items" match="Item" use="someId"/> 

<xsl:template match="/"> 
<ListOfItems> 
    <In_A_NotIn_B> 
    <xsl:apply-templates select="Item"> 
    <xsl:with-param name="otherDocument" select="$docB"/> 
    </xsl:apply-templates> 
    </In_A_NotIn_B> 
    <In_B_NotIn_A> 
    <xsl:apply-templates select="Item"> 
    <xsl:with-param name="otherDocument" select="$docA"/> 
    </xsl:apply-templates> 
    </In_B_NotIn_A> 
</ListOfItems> 
</xsl:template> 

<xsl:template match="Item"> 
<xsl:param name="otherDocument"/> 
    <xsl:variable name="SOMEID" select="someId"/> 
    <xsl:if test="empty(key('items', $SOMEID, $otherDocument))"> 
    <xsl:copy-of select="."/> 
    </xsl:if> 
</xsl:template> 

</xsl:stylesheet>` 
2

Ésta es la La hoja de estilo que escribí para comparar dos archivos XML con diferente orden en nodos y atributos, generará dos archivos de texto que contengan la lista ordenada de todos los nodos hoja. Utilice cualquier herramienta de comparación de texto para detectar las diferencias o mejorar el XSLT para hacer lo que desee.

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

<xsl:output method="text" indent="no" omit-xml-declaration="yes" name="output" /> 

<xsl:param name="OTHERFILENAME">xml_file_to_diff.xml</xsl:param> 
<xsl:param name="ORIGINAL_OUTPUT_FILENAME">ORIGINAL.txt</xsl:param> 
<xsl:param name="OTHER_OUTPUT_FILENAME">OTHER.txt</xsl:param> 

<xsl:template match="/"> 
    <xsl:call-template name="convertXMLHierarchyToFullPath"> 
     <xsl:with-param name="node" select="*"/> 
     <xsl:with-param name="filename" select="$ORIGINAL_OUTPUT_FILENAME"/> 
    </xsl:call-template> 
    <xsl:call-template name="convertXMLHierarchyToFullPath"> 
     <xsl:with-param name="node" select="document($OTHERFILENAME)/*"/> 
     <xsl:with-param name="filename" select="$OTHER_OUTPUT_FILENAME"/> 
    </xsl:call-template> 
</xsl:template> 

<xsl:template name="convertXMLHierarchyToFullPath"> 
    <xsl:param name="node"/> 
    <xsl:param name="filename"/> 

    <xsl:variable name="unorderedFullPath"> 
     <xsl:apply-templates select="$node"/> 
    </xsl:variable> 

    <xsl:result-document href="{$filename}" format="output"> 
     <xsl:for-each select="$unorderedFullPath/*"> 
      <xsl:sort select="@path" data-type="text"/> 
      <xsl:value-of select="@path"/> 
      <xsl:text>&#xA;</xsl:text> 
     </xsl:for-each> 
    </xsl:result-document> 
</xsl:template> 

<xsl:template match="*"> 
    <xsl:if test="not(*)"> 
     <leaf> 
      <xsl:attribute name="path"> 
       <xsl:for-each select="ancestor-or-self::*"> 
        <xsl:value-of select="name()"/> 
        <xsl:for-each select="@*"> 
         <xsl:sort select="name()" data-type="text"/> 
         <xsl:text>[</xsl:text> 
         <xsl:value-of select="name()"/> 
         <xsl:text>:</xsl:text> 
         <xsl:value-of select="."/> 
         <xsl:text>]</xsl:text> 
        </xsl:for-each> 
        <xsl:text>/</xsl:text> 
       </xsl:for-each> 
       <xsl:value-of select="."/> 
      </xsl:attribute> 
     </leaf> 
    </xsl:if> 
    <xsl:apply-templates select="*"/> 
</xsl:template> 

1

Encontrado este post últimamente, pero de todos modos voy a compartir mi solución para este tipo de problema. Tenía las mismas necesidades que @Vincent: comparar 2 archivos XML diferentes y ver rápidamente las diferencias entre ellos. Un diff rápido tenía demasiadas líneas coincidentes porque los archivos no estaban ordenados, así que decidí ordenar los archivos usando XSLT y luego comparar los dos archivos xml manualmente usando WinMerge por ejemplo (un simple diff de unix también puede hacer el trabajo).

Aquí es el XSLT que ordenar mi archivo XML:.

<?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" encoding="UTF-8"/> 

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

</xsl:stylesheet>