Помощь в объединении данных XML - PullRequest
1 голос
/ 07 октября 2009

У меня есть два XMLDocuments, которые содержат некоторую похожую информацию, но есть другие узлы, которые содержат различную информацию между ними.

Я использую XMLSerialization, чтобы поместить мои данные в структуру, как показано здесь

Я знаю, что вы можете объединить XML-файлы, используя DataSet, как показано здесь , но я хочу каким-то образом сериализовать первый документ, который я вижу, в мой класс, а затем добавить второй документ в мою структуру класса.

Есть идеи, как это сделать, или есть лучший подход? Во втором документе, где информация похожа, я с удовольствием перезаписываю ее данными второго документа, например, у каждого документа есть DATE, поэтому мое свойство Date может совпадать со вторым документом.

Вот данные

<ROOT>
<ID>2</ID>
<PART>4a</PART>
<NAME>JEFF</NAME>
<ADDRESS>
    <ST>10001</ST>
    <ID>123456789</ID>
</ADDRESS>
<PARTNUMBER>001</PARTNUMBER>
<DATE>2009 -06-05T16.18.05</DATE>
</ROOT>


<ROOT>
<ID>2</ID>
<PART>4b</PART>
<NAME>JEFF</NAME>
<RELATIVE>
    <ST>10001</ST>
    <ID>1234567890QWERTYUIOP</ID>
</RELATIVE>
<PARTNUMBER>002</PARTNUMBER>
<DATE>2009 -06-05T16.17.41</DATE>
</ROOT>

Ответы [ 2 ]

4 голосов
/ 08 октября 2009

Вы можете сделать что-то подобное:

void Main()
{
    string xml1 = @"<ROOT>
    <ID>2</ID>
    <PART>4a</PART>
    <NAME>JEFF</NAME>
    <ADDRESS>
        <ST>10001</ST>
        <ID>123456789</ID>
    </ADDRESS>
    <PARTNUMBER>001</PARTNUMBER>
    <DATE>2009 -06-05T16.18.05</DATE>
    </ROOT>";


    string xml2 = @"<ROOT>
    <ID>2</ID>
    <PART>4b</PART>
    <NAME>JEFF</NAME>
    <RELATIVE>
        <ST>10001</ST>
        <ID>1234567890QWERTYUIOP</ID>
    </RELATIVE>
    <PARTNUMBER>002</PARTNUMBER>
    <DATE>2009 -06-05T16.17.41</DATE>
    </ROOT>";

    var doc1 = XDocument.Parse(xml1);
    var doc2 = XDocument.Parse(xml2);

    XDocument doc = MergeDocuments(doc1, doc2);
    doc.Dump();
}

static XDocument MergeDocuments(XDocument doc1, XDocument doc2)
{
    var root = MergeElements(doc1.Root, doc2.Root);
    return new XDocument(root);
}

static XElement MergeElements(XElement e1, XElement e2)
{
    var attrComparer = new XAttributeEqualityComparer();
    var nameComparer = new XNameComparer();

    var attributes = e2.Attributes().Union(e1.Attributes(), attrComparer).Cast<XNode>();

    var elements1 = e1.Elements().OrderBy(e => e.Name, nameComparer).ToArray();
    var elements2 = e2.Elements().OrderBy(e => e.Name, nameComparer).ToArray();
    var elements = new List<XNode>();
    int i1 = 0, i2 = 0;
    while (i1 < elements1.Length && i2 < elements2.Length)
    {
        XElement e = null;
        int compResult = nameComparer.Compare(elements1[i1].Name, elements2[i2].Name);
        if (compResult < 0)
        {
            e = elements1[i1];
            i1++;
        }
        else if (compResult > 0)
        {
            e = elements2[i2];
            i2++;
        }
        else
        {
            e = MergeElements(elements1[i1], elements2[i2]);
            i1++;
            i2++;
        }
        elements.Add(e);
    }
    while (i1 < elements1.Length)
    {
        elements.Add(elements1[i1]);
        i1++;
    }
    while (i2 < elements2.Length)
    {
        elements.Add(elements2[i2]);
        i2++;
    }

    var nodes = attributes.Concat(elements).ToArray();
    string value = null;
    if (elements.Count == 0)
    {
        if (!string.IsNullOrEmpty(e1.Value))
            value = e1.Value;
        if (!string.IsNullOrEmpty(e2.Value))
            value = e2.Value;
    }
    if (value != null)
        return new XElement(e1.Name, nodes, value);
    else
        return new XElement(e1.Name, nodes);
}

class XNameComparer : IComparer<XName>
{
    public int Compare(XName x, XName y)
    {
        int result = string.Compare(x.Namespace.NamespaceName, y.Namespace.NamespaceName);
        if (result == 0)
            result = string.Compare(x.LocalName, y.LocalName);
        return result;
    }
}

class XAttributeEqualityComparer : IEqualityComparer<XAttribute>
{
    public bool Equals(XAttribute x, XAttribute y)
    {
        return x.Name == y.Name;
    }

    public int GetHashCode(XAttribute x)
    {
        return x.Name.GetHashCode();
    }
}
3 голосов
/ 02 декабря 2009

Томас ответил действительно здорово. Однако, хотя он отлично работал с данным XML, я обнаружил, что у него есть некоторые проблемы с XML с атрибутами (хотя код теоретически справляется с этим).

Однако эта строка выдает исключение InvalidCastException при попытке преобразования из XAttribute в XNode:

var nodes = attributes.Concat(elements).ToArray();

Тем не менее я обнаружил, что следующие изменения работают для меня. Вместо

var attributes = e2.Attributes().Union(e1.Attributes(), attrComparer).Cast<XNode>();
...
var nodes = attributes.Concat(elements).ToArray();
...
if (value != null)
    return new XElement(e1.Name, nodes, value);
else
    return new XElement(e1.Name, nodes);

Попробуйте это:

var attributes = e2.Attributes().Union(e1.Attributes(), attrComparer);
...
// var nodes = attributes.Concat(elements).ToArray();
...
if (value != null)
    return new XElement(e1.Name, attributes, elements, value);
else
    return new XElement(e1.Name, attributes, elements);

Кажется, работает на меня, хотя я не эксперт по этим вопросам. Это просто к сведению тех, кто сталкивается с этим.

РЕДАКТИРОВАТЬ: Кроме того, обратите внимание, что doc.Dump() не существует для меня и разрывается при компиляции. Я использую .NET 3.5; возможно, ответ Тома зависел от другой версии (3.0?), и это могло также учитывать сообщения об ошибках, которые я получил?

...