How to combine 2 XML files with C #

I have two XML files, and I want to merge these two files into one. But how? I tried a lot, but nothing helps. As you can see, the merged XML left the text from the second attribute if it came from the first XML. Secondly, the Item must be ordered there Id / Name / anyname, which has the first attribute. Third, if node does not exist in XML 2, then it must be created in the same place as in XML 1.

The XML files presented here are just a fragment of the whole XML, there are much more attribute names.

How can I do this using C #?

XML 1

<APPLICATION> <AC> <CLASS Name="Hello1" Capt="do1"/> <CLASS Name="Hello2" Capt="do2"/> <CLASS Name="Hello5" Capt="do5"/> <CLASS Name="Hello8" Capt="do8"/> </AC> <BO> <ITEM Id="1" DefaultValue="name1"/> <ITEM Id="3" DefaultValue="name3"/> <ITEM Id="11" DefaultValue="name11"/> <ITEM Id="12" DefaultValue="name12"> <VAL> <REASON Id="Job1" SecondOne="Hallo"/> </VAL> </ITEM> </BO> <POP Id="Green" Value="Monster"/> <POP Id="Blue" Value="Doggie"/> 

XML 2

 <APPLICATION> <AC> <CLASS Name="Hello1" Capt="dodo1"/> <CLASS Name="Hello2" Capt="dodo2"/> <CLASS Name="Hello3" Capt="dodo3"/> <CLASS Name="Hello9" Capt="dodo9"/> </AC> <CARS Wheel="Fore" Default="45x255xZ"/> <CARS Wheel="BACK" Default="45x255xZ"/> <CARS Wheel="SPARE" Default="45x255xZ"/> <BO> <ITEM Id="1" DefaultValue="namename1"/> <ITEM Id="3" DefaultValue=""/> <ITEM Id="9" DefaultValue="name11"/> <ITEM Id="10" DefaultValue="name12"> <VAL> <REASON Id="Job1" SecondOne="Hallo"/> </VAL> </ITEM> </BO> 

XML should look like this after merging:

 <APPLICATION> <AC> <CLASS Name="Hello1" Capt="dodo1"/> <CLASS Name="Hello2" Capt="dodo2"/> <CLASS Name="Hello3" Capt="dodo3"/> <CLASS Name="Hello5" Capt=""/> <CLASS Name="Hello8" Capt=""/> <CLASS Name="Hello9" Capt="dodo9"/> </AC> <CARS Wheel="Fore" Default="45x255xZ"/> <CARS Wheel="BACK" Default="45x255xZ"/> <CARS Wheel="SPARE" Default="45x255xZ"/> <BO> <ITEM Id="1" DefaultValue="namename1"/> <ITEM Id="3" DefaultValue=""/> <ITEM Id="9" DefaultValue="name11"/> <ITEM Id="10" DefaultValue="name12"> <VAL> <REASON Id="Job1" SecondOne="Hallo"/> </VAL> </ITEM> <ITEM Id="11" DefaultValue=""/> <ITEM Id="12" DefaultValue=""> <VAL> <REASON Id="Job1" SecondOne=""/> </VAL> </ITEM> </BO> <POP Id="Green" Value=""/> <POP Id="Blue" Value=""/> 

Thanx for all the answers, but still I have a problem that I donโ€™t know how to tags, so I canโ€™t hardcode the tags.

I just have to give you an example of how it might look. But the next time I get my XML files, the tags above can be completely different. That's the problem. Therefore, I canโ€™t say the new XElement ("BO", boChildren), because next time this tag no longer exists.

Or I canโ€™t hardcode this ==> var cars = xDocuments.SelectMany (x => x.Root.Elements ("CARS")). Merge (); because the next time I get my XML files, "CARS" no longer exists.

+6
source share
5 answers

I think you can do it with Linq in XML. Create separate queries for each segment (AC, BO, CARS, POP), where you combine them together and then combine them into a new document.

Here is a small snippet to get you started:

 using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace XML_Merge { class Program { static void Main(string[] args) { // load two xdocs var x1 = XDocument.Load("x1.xml"); var x2 = XDocument.Load("x2.xml"); // select the CLASS nodes from each var c1 = x1.Descendants("AC").First().Descendants("CLASS"); var c2 = x2.Descendants("AC").First().Descendants("CLASS"); // this one gives the distinct union of the two, you can put that into the result xdoc. var cComb = c1 .Union(c2) .Distinct(new ClassComparer()) // this uses the IEqualityComparer from below .OrderBy(c => c.Attribute("Name").Value); } } // This is required for Union to work. (Also Intersect etc) class ClassComparer : IEqualityComparer<XElement> { public bool Equals(XElement x, XElement y) { return x.Attribute("Name").Value == y.Attribute("Name").Value; } public int GetHashCode(XElement obj) { return obj.Attribute("Name").Value.GetHashCode(); } } } 

Just repeat for the other nodes in the source documents, and then put it all together.

Good luck

Geert Yang

+7
source

I suggest you not do this with C #. Try using XSLT where you can, which is mainly used for xmls conversion.

if you want to use C #, use the new C # 3.5 / 4 XDocument. It has good LINQ-based syntax that makes it easier to work with.

+3
source

You can use the XmlDocument class. Check out this link: http://support.microsoft.com/kb/311530

0
source

You might have something like this:

 class APPLICATION { public APPLICATION() { this.Classes = new List<CLASS>(); this.Cars = new List<CARS>(); this.Items = new List<ITEM>(); this.Pops = new List<POP>(); } public List<CLASS> Classes { get; set; } public List<CARS> Cars { get; set; } public List<ITEM> Items { get; set; } public List<POP> Pops { get; set; } public override string ToString() { string toString = string.Empty; using (MemoryStream stream = new MemoryStream()) { using (XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8)) { writer.Formatting = Formatting.Indented; writer.Indentation = 5; writer.WriteStartDocument(); writer.WriteStartElement("APPLICATION"); writer.WriteStartElement("AC"); if (this.Classes != null && Classes.Count > 0) { foreach (CLASS c in Classes) { writer.WriteStartElement("CLASS"); writer.WriteAttributeString("Name", c.Name); writer.WriteAttributeString("Capt", c.Capt); writer.WriteEndElement(); //CLASS } } writer.WriteEndElement(); //AC if (this.Cars != null && Cars.Count > 0) { foreach (CARS c in Cars) { writer.WriteStartElement("CARS"); writer.WriteAttributeString("Wheel", c.Wheel); writer.WriteAttributeString("Default", c.Default); writer.WriteEndElement(); //CARS } } writer.WriteStartElement("BO"); if (this.Items != null && Items.Count > 0) { foreach (ITEM c in Items) { writer.WriteStartElement("ITEM"); writer.WriteAttributeString("Id", c.Id); writer.WriteAttributeString("DefaultValue", c.DefaultValue); if (c.Reason != null) { writer.WriteStartElement("VAL"); writer.WriteStartElement("REASON"); writer.WriteAttributeString("Id", c.Reason.Id); writer.WriteAttributeString("SecondOne", c.Reason.SecondOne); writer.WriteEndElement(); //ITEM writer.WriteEndElement(); //VAL } writer.WriteEndElement(); //ITEM } } writer.WriteEndElement(); //BO writer.WriteEndElement(); //APPLICATION writer.WriteEndDocument(); writer.Flush(); stream.Position = 0; XmlTextReader reader = new XmlTextReader(stream); reader.MoveToContent(); toString = reader.ReadOuterXml(); writer.Close(); stream.Close(); } } return toString; } } public class REASON { public REASON() { Id = string.Empty; SecondOne = string.Empty; } public string Id { get; set; } public string SecondOne { get; set; } } public class ITEM { public ITEM() { Id = string.Empty; DefaultValue = string.Empty; } public string Id { get; set; } public string DefaultValue { get; set; } public REASON Reason { get; set; } } public class CARS { public CARS() { Wheel = string.Empty; Default = string.Empty; } public string Wheel { get; set; } public string Default { get; set; } } public class CLASS { public CLASS() { Name = string.Empty; Capt = string.Empty; } public string Name { get; set; } public string Capt { get; set; } } public class POP { public POP() { Id = string.Empty; Value = string.Empty; } public string Id { get; set; } public string Value { get; set; } } 

And use it as follows:

 APPLICATION application = new APPLICATION(); application.Classes = ... //Populate this with classes read from xml 1 and 2. application.Cars = ... //Populate this with cars read from xml 1 and 2. application.Items = ... //Populate this with items read from xml 1 and 2. application.Pops = ... //Populate this with pops read from xml 1 and 2. string yourXmlString = application.ToString(); 
0
source

Here is some code for you to get started. However, you have some very specific requirements for how combined elements are generated from the original elements. You will have to implement this in the extension method:

 var xDocuments = new[] { XDocument.Parse(xml1), XDocument.Parse(xml2) }; var acChildren = xDocuments.SelectMany(x => x.Root.Elements("AC")) .SelectMany(x => x.Elements()).Merge(); var cars = xDocuments.SelectMany(x => x.Root.Elements("CARS")).Merge(); var boChildren = xDocuments.SelectMany(x => x.Root.Elements("BO")) .SelectMany(x => x.Elements()).Merge(); var pops = xDocuments.SelectMany(x => x.Root.Elements("POP")).Merge(); var mergedXDocument = new XDocument( new XElement("APPLICATION", new XElement("AC", acChildren), cars, new XElement("BO", boChildren), pops ) ); 

Here is the template for the extension method:

 public static class Extensions { public static IEnumerable<XElement> Merge(this IEnumerable<XElement> xElements) { // Implement the requirement: // "the merged XML has left the text from the second attribute if it came from // the first XML" } } 
0
source

Source: https://habr.com/ru/post/891289/


All Articles