如何使用C#合并2个XML文件

时间:2011-06-24 12:28:52

标签: c# xml linq

我有两个XML文件,我想将这两个文件合并为一个。 但是怎么样?我已经尝试了很多,但没有任何帮助。 正如您所看到的,合并后的XML如果来自第一个XML,则会从第二个属性中删除该文本。 第二个元素必须由第一个属性的Id / Name / whatevername命令。 第三,如果XML 2中不存在节点,则必须在与XML 1中相同的位置创建节点。

这里显示的XML文件只是整个XML的一个片段,还有更多属性名称。

如何使用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应如下所示:

<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的所有答案,但我仍然有一个问题,我不知道标记名是怎么回事,所以我不能硬编码标签。

我只需要举例说明它的样子。但是下次我获取XML文件时,上面的标签可能完全不同。这就是问题所在。所以我不能说新的XElement(“BO”,boChildren),因为下次这个标签不再存在了。

或者我不能硬编码==&gt; var cars = xDocuments.SelectMany(x =&gt; x.Root.Elements(“CARS”))。Merge();因为下次我得到我的XML文件“CARS”不再存在。

5 个答案:

答案 0 :(得分:7)

我认为你可以用Linq to XML做到这一点。为每个段(AC,BO,CARS,POP)创建单独的查询,将它们连接在一起,然后将它们放在一个新文档中。

这是一个让你开始的小片段:

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(); }
    }
}

只需重复源文档中的其他节点,然后将它们全部放在一起。

祝你好运,

格特 - 扬

答案 1 :(得分:3)

我建议你不要用C#做那件事。尝试尽可能使用XSLT,主要用于转换xmls。

如果你想使用C#,那么使用新的C#3.5 / 4 XDocument。它具有很好的基于LINQ的语法,使其更易于使用。

答案 2 :(得分:0)

您可以使用XmlDocument类。 检查此链接: http://support.microsoft.com/kb/311530

答案 3 :(得分:0)

你可以这样:

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; }
}

并像这样使用它:

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();

答案 4 :(得分:0)

以下是一些可以帮助您入门的代码。但是,您有一些非常具体的要求如何从原始元素生成合并元素。您必须在扩展方法中实现它:

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
  )
);

以下是扩展方法的模板:

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"
  }

}