2

I want to merge 2 XML files using XSLT when there is matching 'id' attribute.

myFile1.xml (This is the first input file)

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <test>
      <node>
        <type id="a">
          <name>joe</name>
          <name>kill</name>
        </type>
      </node>
      <node>
        <type id="b">
          <name>sam</name>
        </type>
      </node>
    </test>

myFile2.xml (This is the second input file)

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <test>
      <node>
        <type id="a">
        <name>jill</name>
        <name>kill</name>
        </type>
      </node>
    </test>

mergeOutput.xml (This is expected output where id is matched and 2 files are merged)

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <test>
      <node>
        <type id="a">
          <name>joe</name>
         <name>jill</name> 
          <name>Kill</name>
        </type>
      </node>
      <node>
        <type id="b">
          <name>sam</name>
        </type>
      </node>
    </test>

Any XSLT code or link to the code would be helpful. I am not having XSLT knowledge and using this merge to get things working.

  • Can there be more than one `` element in the same file with the same `id`? – Ian Roberts Aug 31 '12 at 13:48
  • (Reply to Ian Roberts) - There are multiple elements in the file but each with unique id attribute. – Prashant Trivedi Aug 31 '12 at 14:00
  • can do this using LINQ2XML in seconds...but not in xslt..can u use LINQ2XML for this IF u r a c# dev – Anirudha Aug 31 '12 at 14:35
  • In one input file, the element with id="a" has two children; in the other file it has 2 different children; but in the result it has three out of these four. I don't understand the logic. – Michael Kay Aug 31 '12 at 14:54
  • @MichaelKay, The OP thinks that "Kill" and "kill" are two equal strings -- probably a typo. – Dimitre Novatchev Aug 31 '12 at 18:26
  • Dimitre is right. Kill and kill are equal strings and therefore not repeated in the merged file. – Prashant Trivedi Aug 31 '12 at 19:38
  • Thank you for 2 replies here on my post. This weekend I searched for existing solution. Sorry I should have done this before but I took time to understand existing solutions because of my limited knowledge of XSLT. Please see my answer below, it still require a little tweak to avoid the duplicate rows. – Prashant Trivedi Sep 02 '12 at 21:38

3 Answers3

0

XSLT 2.0 solution:

<xsl:template name="main">
  <test>
    <node>
      <xsl:for-each-group select="(doc('myFile1.xml'), doc('myFile2.xml'))/test/node/type"
                          group-by="@id">
        <type id="{@id}">
          <xsl:copy-of select="current-group()/*"/>
        </type>
      </xsl:for-each-group>
    </node>
  </test>
</xsl:template>
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
0

You can do this in LINQ2XML

XElement doc1=XElement.Load("myFile1.xml");
XElement doc2=XElement.Load("myFile2.xml");
XElement doc3=XElement.Load("myFile1.xml");//will contain mergeOutput

var dec=doc3.Descendants().Elements("type").ToList();
int i=0;
foreach(XElement elm1 in doc1.Descendants().Elements("type"))
foreach(XElement elm2 in doc2.Descendants().Elements("type"))
if(elm1.Attribute("id").Value==elm2.Attribute("id").Value)
dec[i++].Add(elm2.Elements());

doc3;//your mergeOutput
Anirudha
  • 32,393
  • 7
  • 68
  • 89
0
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes" />
  <xsl:variable name="with" select="'myFile2.xml'" />
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="//type">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
      <xsl:variable name="info" select="document($with)/test/node/type[@id=current()/@id]/." />
     <xsl:for-each select="$info/*">
          <xsl:copy-of select="." />
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:transform>

The output of this transform is (See kill is duplicated) -

<?xml version="1.0" encoding="ISO-8859-1"?>
<test>
    <node>
        <type id="a">
            <name>joe</name>
            <name>kill</name>
            <name>jill</name>
            <name>kill</name>
        </type>
    </node>
    <node>
        <type id="b">
            <name>sam</name>
        </type>
    </node>
</test>
  • I used the above transform to get intermediate XML with duplicates and then I wrote another one to find and delete duplicate under given /type/@id – Prashant Trivedi May 29 '13 at 05:46