Search code examples
xslttreecomparenodes

Compare two xml tree nodes and find if a node with a value exists in another using xslt


I have an XML input which is merged format of two xmls:

<DATA>
<RECORDS1>
    <RECORD>
        <id>11</id>
        <value>123</value>
    </RECORD>
    <RECORD>
        <id>33</id>
        <value>321</value>
    </RECORD>
    <RECORD>
        <id>55</id>
        <value>121113</value>
    </RECORD>
    ...
</RECORDS1>
<RECORDS2>
    <RECORD>
        <id>11</id>
        <value>123</value>
    </RECORD>
    <RECORD>
        <id>33</id>
        <value>323</value>
    </RECORD>
    <RECORD>
        <id>44</id>
        <value>12333</value>
    </RECORD>
    ...
</RECORDS2>

I need to copy in the output the records in RECORDS1 provided:

  1. The records in RECORDS1 doesnot exist in RECORDS2
  2. The records in RECORDS1 exists in RECORDS2 but the value is different

Plus if the output could be extended such with an extra field with value as NEW (when does not not exist) as CHANGE (when exists but value is different)

Output

<DATA>
<RECORDS>
    <RECORD>
        <id>33</id>
        <value>321</value>
        <kind>Change</kind>
    </RECORD>
    <RECORD>
        <id>55</id>
        <value>121113</value>
        <kind>New</kind>
    </RECORD>
    ...
</RECORDS>

I have applied FOR Loop but as the variable in xslt cant be reset hence it doesnot work.

Any ideas?


Solution

  • Perhaps

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="2.0">
        
      <xsl:output indent="yes"/>
    
      <xsl:key name="rec2-complete" match="RECORDS2/RECORD" use="concat(id, '|', value)"/>
      <xsl:key name="rec2-id" match="RECORDS2/RECORD" use="id"/>
      
      <xsl:template match="DATA">
          <xsl:apply-templates select="RECORDS1/RECORD[not(key('rec2-complete', concat(id, '|', value)))]"/>
      </xsl:template>
      
      <xsl:template match="RECORDS1/RECORD">
          <xsl:copy>
              <xsl:copy-of select="node()"/>
              <merged>
                  <xsl:value-of select="if (key('rec2-id', id)/value != value) then 'changed' else 'new'"/>
              </merged>
          </xsl:copy>
      </xsl:template>
      
    </xsl:stylesheet>
    

    implements the requirements.

    Or, to construct the complete result you have shown now, use

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="2.0">
        
      <xsl:output indent="yes"/>
    
      <xsl:key name="rec2-complete" match="RECORDS2/RECORD" use="concat(id, '|', value)"/>
      <xsl:key name="rec2-id" match="RECORDS2/RECORD" use="id"/>
      
      <xsl:template match="DATA">
          <xsl:copy>
              <RECORDS>
                  <xsl:apply-templates select="RECORDS1/RECORD[not(key('rec2-complete', concat(id, '|', value)))]"/>
              </RECORDS>
          </xsl:copy>
      </xsl:template>
      
      <xsl:template match="RECORDS1/RECORD">
          <xsl:copy>
              <xsl:copy-of select="node()"/>
              <kind>
                  <xsl:value-of select="if (key('rec2-id', id)/value != value) then 'change' else 'new'"/>
              </kind>
          </xsl:copy>
      </xsl:template>
      
    </xsl:stylesheet>