XML Diff: How to generate XML diff using XSLT?

Viewed 10217

I would like to compute the diff between two XML files or nodes using XSL/XSLT. Is there any stylesheet readily available or any simple way of doing it?

7 Answers

This is not a mystery! Here are the general steps:

  1. @carillonator is right about how XSLT processes documents. So to make it easier we combine the two versions of your documents into a single document you can use to run your XSLT diff on ( You can do this via the command line with bash, or with whatever programming language you are using, or even another XSLT transform [pipe] ). It's just an encapsulation:

    <diff_container>
        <version1>
          ... first version here
        </version1>
        <version2>
          ... second version here
        </version2>
    </diff_container>
    
  2. We then run this document through our XSLT diff, the XSLT then has the job of simply traversing the tree and comparing nodes between the two versions. This can go from very simple ( Was an element changed? Moved? Removed? ) to semi complex. A good understanding of XPath makes this fairly simple.

    Like some said before, your working inside a different environment so you are limited compared to tools like Diff Dog. However the benefit of having the algorithm in XSLT can have real value too.

Hope this helped. Cheers!

If what you mean by diff is something like checking whether items exist in one document (or node) but not another, you can use xpath key() function with a third parameter

<?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>`
Related