【问题标题】:Help Merging XML Data帮助合并 XML 数据
【发布时间】:2010-12-04 16:39:50
【问题描述】:

我有两个包含一些相似信息的 XMLDocuments,但还有其他节点包含两者之间的不同信息。

我正在使用 XMLSerialization 将我的数据放入一个结构中,如图所示 here

我知道您可以使用 DataSet 合并 XML 文件,如 here 所示,但我想以某种方式将我看到的第一个文档序列化到我的类中,然后将第二个文档附加到我的类结构中。

任何想法如何做到这一点或有更好的方法吗?在信息相似的第二个文档上,我很乐意用第二个文档数据覆盖它,例如每个文档都有一个 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>

【问题讨论】:

    标签: c# linq c#-3.0 xml-serialization linq-to-xml


    【解决方案1】:

    你可以这样做:

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

    【讨论】:

    • 哇!你写的还是使用 LinqPad?虽然我不完全理解,但它有效!
    • 我一开始用的是 LinqPad,后来用 VS 调试了 ;)。基本上,要合并一个元素,我先合并它们的所有属性,然后再合并它们的所有子元素。我递归合并具有相同名称的子元素
    • 谢谢,可能需要查看 LinqPad。幸运的是我没有属性,所以不需要检查它们或将它们与元素连接,所以我想我可以把它拿出来。感谢您的帮助。
    • 出于兴趣,LinqPad 是否具有某种合并功能,因此您提供了 2 个 xml 文件并由它编写代码?
    • 不,绝对不是... LinqPad 不生成代码,它只是一个无需创建完整 Visual Studio 项目即可进行快速测试的工具
    【解决方案2】:

    Thomas 的回答真的很棒。然而,尽管它在给定的 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?),这也可能解释了我收到的错误消息?

    【讨论】:

    • 注意:.Dump() 是 Linqpad 使用的一种方法。这是一个在设计时执行 linq 查询的应用程序
    猜你喜欢
    • 1970-01-01
    • 2011-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多