Manas Saha
Manas Saha

Reputation: 1497

Need to merge attributes from 2 XMLs into one

I have two XMLs like this.

XML1

<RPM>
  <CAI ID="101" Name="Guaranteed Payments"/>
  <CAI ID="102" Name="Sports Recreation"/> 
</RPM>

XML2

<RPM>
  <CAI ID="102" Active="False"/>
  <CAI ID="103" Active="True"/> 
</RPM>

I have to write a C# code which will merge the attributes of the two XMLs into one, based on the CAI ID. For example, both XMLs has a node with CAI ID 102, so the final XML will be,

RESULT

    <RPM>
       <CAI ID="101" Name="Guaranteed Payments"/>
       <CAI ID="102" Name="Sports Recreation" Active="False"/>
       <CAI ID="103" Active="True"/>
    </RPM>

Upvotes: 1

Views: 658

Answers (2)

L.B
L.B

Reputation: 116168

string xml1 = @"
    <RPM>
        <CAI ID=""101"" Name=""Guaranteed Payments""/>
        <CAI ID=""102"" Name=""Sports Recreation""/> 
    </RPM>";

string xml2 = @"
    <RPM>
        <CAI ID=""102"" Active=""False""/>
        <CAI ID=""103"" Active=""True""/> 
    </RPM>";

XDocument xDoc1 = XDocument.Load(new StringReader(xml1));
XDocument xDoc2 = XDocument.Load(new StringReader(xml2));
var cais = xDoc1.Descendants("CAI")
          .Concat(xDoc2.Descendants("CAI"))
          .GroupBy(x => x.Attribute("ID").Value)
          .Select(x => x.SelectMany(y => y.Attributes()).DistinctBy(a => a.Name))
          .Select(x => new XElement("CAI", x));

string xml = new XElement("RPM", cais).ToString();

You can find the full implementation of DistinctBy by Jon Skeet here

public static partial class MyExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
    {
        HashSet<TKey> knownKeys = new HashSet<TKey>();
        foreach (T element in source)
        {
            if (knownKeys.Add(keySelector(element)))
            {
                yield return element;
            }
        }
    }
}

Upvotes: 0

Chris
Chris

Reputation: 3162

I'd be tempted to do something like this, deserialize both XML files into classes, then combine them through code.

So the XML definition class would be the end product you want:

[XmlTypeAttribute]
[XmlRootAttribute("RPM")]
public class RPMConfiguration
{

    [XmlElementAttribute("CAI")]
    public CAI[] CAIList{ get; set; }


}

[XmlTypeAttribute]
public class CAI
{
    [XmlAttributeAttribute("ID")]
    public int ID { get; set; }

    [XmlAttributeAttribute("Name")]
    public string Name { get; set; }

    [XmlAttributeAttribute("Active")]
    public string Active{ get; set; }
}

Then you'd deserialize the xml strings like so:

public static object Deserialize(string xml)
{
    var deserializer = new System.Xml.Serialization.XmlSerializer(typeof(RPMConfiguration));
    using (var reader = XmlReader.Create(new StringReader(xml)))
    {
        return (RPMConfiguration)deserializer.Deserialize(reader);
    }
}

At which point, you have two RPMConfiguration objects, with a List of CAI objects, which you can then loop through and match on ID, treat one as the master collection and copy any attributes that are missing into it.

Once you're done. Just serialize the config back into the XML and hey presto, One complete XML File.

Upvotes: 2

Related Questions