tags:

views:

331

answers:

3

Here's my problem.I have 2 xmlfiles with identical structure, with the second xml containing only few node compared to first.

File1

  <root>
    <alpha>111</alpha>
    <beta>22</beta>
    <gamma></gamma>
    <delta></delta>
  </root>

File2

 <root>
    <beta>XX</beta>
    <delta>XX</delta>
 </root>

This's what the result should look like

 <root>
    <alpha>111</alpha>
    <beta>22</beta>
    <gamma></gamma>
    <delta>XX</delta>
 </root>

Basically if the node contents of any node in File1 is blank then it should read the values from File2(if it exists, that is).

I did try my luck with Microsoft XmlDiff API but it didn't work out for me(the patch process didn't apply changes to the source doc). Also I'm a bit worried about the DOM approach that it uses, because of the size of the xml that I'll be dealing with. Can you please suggest a good way of doing this. I'm using C# 2

+1  A: 

This merge seems very specific.

If that is the case, just write some code to load both xml files and apply the changes as you described.

Pyrolistical
+2  A: 

In XSLT you can use the document() function to retrieve nodes from File2 if you encounter an empty node in File1. Something like:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:template match="root/*[.='']">
    <xsl:variable name="file2node">
        <xsl:copy-of select="document('File2.xml')/root/*[name()=name(current())]"/>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="$file2node != ''">
        <xsl:copy-of select="$file2node"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="*">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
Alastair
The first template, and especially expressions like: "root/*[.='']" can be very expensive, because they need to calculate the complete string contents of any element and this is the result of concatenating all text node descendents of this element. That means complete subtrees will be traversed.
Dimitre Novatchev
+2  A: 

Here is a little bit simpler and more efficient solution that that proposed by Alastair (see my comment to his solution).

This transformation:

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

    <xsl:variable name="vFile2"
      select="document('File2.xml')"/>

    <xsl:template match="*">
      <xsl:copy>
        <xsl:copy-of select="@*"/>
      <xsl:apply-templates/>
     </xsl:copy>
    </xsl:template>

    <xsl:template match="*[not(text())]">
      <xsl:copy>
     <xsl:copy-of
       select="$vFile2/*/*[name() = name(current())]/text()"/>
     </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<root>
    <alpha>111</alpha>
    <beta>22</beta>
    <gamma></gamma>
    <delta></delta>
</root>

produces the wanted result:

<root>
    <alpha>111</alpha>
    <beta>22</beta>
    <gamma></gamma>
    <delta>XX</delta>
</root>
Dimitre Novatchev
This works fine. Thanks Dimitre
HashName