Search code examples
xmlxsltxslt-2.0xslt-3.0

Need help on value comparison using XSLT - Compare 2 parent nodes


I have a XML which have two parent nodes (Base, Sub). I need to write a XSLT to get the values for below condition.

Condition: Need to get different elements in both parents.

Input XML:

<?xml version="1.0" encoding="UTF-8"?>
<Data>
  <Base>
    <Student_ID>1234</Student_ID>
    <Student_ID>1267</Student_ID>
    <Student_ID>1890</Student_ID>
    <Student_ID>5678</Student_ID>
    <Student_ID>6743</Student_ID>
    <Student_ID>8743</Student_ID>
  </Base>
  <Sub>
    <Student_ID>5678</Student_ID>
    <Student_ID>6743</Student_ID>
    <Student_ID>3226</Student_ID>
    <Student_ID>8123</Student_ID>
  </Sub>
</Data>

Expected Output:

<?xml version="1.0" encoding="UTF-8"?>
<Data>
    <Student_ID>1234</Student_ID>
    <Student_ID>1267</Student_ID>
    <Student_ID>1890</Student_ID>
    <Student_ID>8743</Student_ID>
    <Student_ID>3226</Student_ID>
    <Student_ID>8123</Student_ID>
</Data>

Solution

  • In XSLT 3 you could also consider xsl:merge, that would allow you to handle duplicates in each branch; drawback is that merging requires sorting input sequences on merge key(s) so the result of

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
      expand-text="yes"
        exclude-result-prefixes="#all"
        version="3.0">
    
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:template match="Data">
        <xsl:copy>
          <xsl:merge>
            <xsl:merge-source name="base" select="Base/Student_ID" sort-before-merge="yes">
              <xsl:merge-key select="."/>
            </xsl:merge-source>
            <xsl:merge-source name="sub" select="Sub/Student_ID" sort-before-merge="yes">
              <xsl:merge-key select="."/>
            </xsl:merge-source>
            <xsl:merge-action>
              <xsl:if test="count(current-merge-group('base')) = 0 and count(current-merge-group('sub')) = 1 or count(current-merge-group('base')) = 1 and count(current-merge-group('sub')) = 0">
                <xsl:copy-of select="current-merge-group()"/>
              </xsl:if>
            </xsl:merge-action>
          </xsl:merge>
        </xsl:copy>
      </xsl:template>
      
    </xsl:stylesheet>
    

    has the right elements but in sorted order:

    <Data>
       <Student_ID>1234</Student_ID>
       <Student_ID>1267</Student_ID>
       <Student_ID>1890</Student_ID>
       <Student_ID>3226</Student_ID>
       <Student_ID>8123</Student_ID>
       <Student_ID>8743</Student_ID>
    </Data>
    

    Of course we could re-sort into original input order using

     <xsl:template match="Data">
        <xsl:copy>
         <xsl:variable name="unique-ids" as="element(Student_ID)*">
          <xsl:merge>
            <xsl:merge-source name="base" select="Base/Student_ID" sort-before-merge="yes">
              <xsl:merge-key select="."/>
            </xsl:merge-source>
            <xsl:merge-source name="sub" select="Sub/Student_ID" sort-before-merge="yes">
              <xsl:merge-key select="."/>
            </xsl:merge-source>
            <xsl:merge-action>
              <xsl:if test="count(current-merge-group('base')) = 0 and count(current-merge-group('sub')) = 1 or count(current-merge-group('base')) = 1 and count(current-merge-group('sub')) = 0">
                <xsl:sequence select="current-merge-group()"/>
              </xsl:if>
            </xsl:merge-action>
          </xsl:merge>
         </xsl:variable>
         <xsl:copy-of select="$unique-ids/."/>
        </xsl:copy>
      </xsl:template>