Как объединить 2 XML-файла с C # - PullRequest
6 голосов
/ 24 июня 2011

У меня есть два 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=""/>

Спасибо за все ответы, но все же у меня есть проблема, которую я не знаюкак тэги, так что я не могу жестко закодировать тэги.

Я просто должен дать вам пример, как это может выглядеть.Но в следующий раз, когда я получу свои XML-файлы, теги сверху могут быть совершенно другими.Вот где проблема.Поэтому я не могу сказать новый XElement ("BO", boChildren), потому что в следующий раз этот тег больше не будет существовать.

Или я не могу жестко закодировать этот ==> var cars = xDocuments.SelectMany(x => x.Root.Elements ("CARS")). Merge ();потому что в следующий раз, когда я получу свои XML-файлы, «АВТОМОБИЛИ» больше не будут существовать.

Ответы [ 5 ]

7 голосов
/ 24 июня 2011

Я думаю, что вы можете сделать это с 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(); }
    }
}

Просто повторите для других узлов в исходных документах, а затем соедините все вместе.

Удачи,

Герт-Ян

3 голосов
/ 24 июня 2011

Я предлагаю вам не делать этого с C #.Попробуйте использовать XSLT, где это возможно, что в основном используется для преобразования xmls.

, если вы хотите использовать C #, тогда используйте новый C # 3.5 / 4 XDocument.У него хороший синтаксис на основе LINQ, с которым легче работать.

0 голосов
/ 24 июня 2011

Вот код, с которого можно начать. Однако у вас есть очень специфические требования к тому, как объединенные элементы генерируются из исходных элементов. Вы должны будете реализовать это в методе расширения:

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

}
0 голосов
/ 24 июня 2011

У вас может быть что-то вроде этого:

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();
0 голосов
/ 24 июня 2011

Вы можете использовать класс XmlDocument.Проверьте эту ссылку: http://support.microsoft.com/kb/311530

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...