Thusara Deemantha
Thusara Deemantha

Reputation: 60

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>

Upvotes: 0

Views: 75

Answers (4)

Michael Kay
Michael Kay

Reputation: 163352

Another option:

<xsl:for-each select="distinct-values(*/Student_ID)">
  <Student_ID>{.}</Student_ID>
</xsl:for-each>

Upvotes: 0

Martin Honnen
Martin Honnen

Reputation: 167581

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>

Upvotes: 2

michael.hor257k
michael.hor257k

Reputation: 117003

Another way you could look at it:

XSLT 2.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/Data">
    <xsl:copy>
        <xsl:for-each-group select="*/Student_ID" group-by=".">
            <xsl:if test="count(current-group())=1">
                <xsl:copy-of select="."/>
            </xsl:if>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Note that this assumes there are no duplicates within each branch.

Upvotes: 0

y.arazim
y.arazim

Reputation: 3171

Maybe something like this?

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

<xsl:key name="student" match="Student_ID" use="." />

<xsl:template match="/Data">
    <xsl:copy>
        <xsl:copy-of select="Base/Student_ID[not(key('student', ., ../../Sub))]"/>
        <xsl:copy-of select="Sub/Student_ID[not(key('student', ., ../../Base))]"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Related Questions