【问题标题】:Merge XML based node value合并基于 XML 的节点值
【发布时间】:2021-05-28 19:05:24
【问题描述】:

我希望合并两个 XML 文件,类似于 What is the fastest way to combine two xml files into one

但我无法理解如何根据节点值(配置节点的域节点值)对它们进行分组和合并 我正在尝试使用 Linq,但它并没有让它变得更容易,即使是 group by 和 where 子句也在那里。

基本上我希望所有 Component 节点(允许重复)都列在同一个 Configuration 节点下,其中 Domain 名称节点值是相等。

换句话说,下面的例子: 结果 XML 有两个 Configuration 节点, 一个具有MyDom01 另一个是MyDom02 在每个配置下,我都有一个 Components 节点,其中列出了所有 Component

这可能吗?

一个.XML

<System>
    <Configurations>
    
        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>16</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>8</Size>
                </Component>
            </Components>
        </Configuration>

        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>HDD</Name>
                    <Size>1</Size>
                </Component>
            </Components>
        </Configuration>
                    
        <Configuration>
            <Domain>MyDom02</Domain>
            <Components>
                <Component>
                    <Name>CPU</Name>
                    <Size>12</Size>
                </Component>
            </Components>
        </Configuration>

    </Configurations>
</System>

另一个.XML

<System>
    <Configurations>
    
        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>128</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>
            </Components>
        </Configuration>
                    
        <Configuration>
            <Domain>MyDom02</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>32</Size>
                </Component>
            </Components>
        </Configuration>

    </Configurations>
</System>

合并的.XML:

<System>
    <Configurations>
    
        <Configuration>
            <Domain>MyDom01</Domain>
            <Components>
                <Component>
                    <Name>Memory</Name>
                    <Size>16</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>8</Size>
                </Component>

                <Component>
                    <Name>HDD</Name>
                    <Size>1</Size>
                </Component>

                <Component>
                    <Name>Memory</Name>
                    <Size>128</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>
                <Component>
                    <Name>CPU</Name>
                    <Size>32</Size>
                </Component>                
            </Components>
        </Configuration>
                    
        <Configuration>
            <Domain>MyDom02</Domain>
            <Components>
                <Component>
                    <Name>CPU</Name>
                    <Size>12</Size>
                </Component>
                <Component>
                    <Name>Memory</Name>
                    <Size>32</Size>
                </Component>
            </Components>
        </Configuration>

    </Configurations>
</System>

【问题讨论】:

    标签: c# xml linq merge


    【解决方案1】:

    使用 Xml Linq:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            const string FILENAME1 = @"c:\temp\test1.xml";
            static void Main(string[] args)
            {
                XDocument doc = XDocument.Load(FILENAME);
                XElement configurations = doc.Descendants("Configuration").FirstOrDefault();
                Dictionary<string, XElement> dict = doc.Descendants("Configuration")
                    .GroupBy(x => (string)x.Element("Domain"), y => y)
                    .ToDictionary(x => x.Key, y => y.FirstOrDefault());
           
    
                XDocument doc1 = XDocument.Load(FILENAME1);
    
                XElement configurations1 = doc1.Descendants("Configurations").FirstOrDefault();
                foreach (XElement configuration1 in configurations1.Elements("Configuration"))
                {
                    string domain = (string)configuration1.Element("Domain");
                    if (dict.ContainsKey(domain))
                    {
                        XElement config = dict[domain];
                        config.Element("Components").Add(configuration1.Descendants("Component"));
                    }
                    else
                    {
                        configurations.Add(configuration1);
                        dict.Add(domain, configuration1);
                    }
                }
            }
        }
    }
    

    【讨论】:

    • @jdwemd 谢谢,但是这种合并配置与它们的 节点中的内容无关。只有具有相同域的配置才能合并在一起。
    • @jdwend 几乎完美。当同一文件中存在另一个具有相同域名的配置时,它无法处理。在示例中添加了这种情况,搜索 HDD。我正在尝试添加它。也许我需要将所有 XML 合并到一个巨大的 XML 中,然后运行这段代码。
    • 我想过,但没想到会发生。需要将域添加到字典中。我将修改代码。它是 else 中的一行。
    【解决方案2】:

    您可以尝试在 XML1 中找到匹配 &lt;Domain&gt;&lt;Components&gt; 元素,然后将 XML2 中的 &lt;Component&gt; 元素添加到其中,如果没有匹配的 &lt;Domain&gt;,则将 XML2 中的 &lt;Configuration&gt; 添加到 XML1 中的 &lt;Configurations&gt;找到了,例如:

    var xdoc1 = XDocument.Load("path/to/xml1.xml");
    var xdoc2 = XDocument.Load("path/to/xml2.xml");
    
    foreach (var item in xdoc2.Descendants("Configuration"))
    {
        var domain = (string)item.Element("Domain");
    
        // try to find <Components> with matching <Domain> in XML1
        var parent = xdoc1.Descendants("Configuration")
                            .Where(o => (string)o.Element("Domain") == domain)
                            .Elements("Components")
                            .FirstOrDefault();
    
        // if such <Components> is found, add <Component> from XML2 to it
        if(parent != null) 
        {
            parent.Add(item.Elements("Components").Elements("Component"));
        }
        // otherwise add <Configuration> from XML2 to <Configurations> in XML1 
        else 
        {
            xdoc1.Root.Element("Configurations").Add(item);
        }
    }
    
    // don't forget to save the modified XML1 if you need to
    // here we only print the merged XML1
    System.Console.WriteLine(xdoc1.ToString());
    

    dotnetfiddle demo

    【讨论】:

    • 这很好,但它似乎是“如果不存在则左连接”设置,我不能保证这是真的。更像是需要“完全外部连接”。我试着根据你的想法来解决这个问题。
    • 嗯...我不明白。最后,这应该将 XML 2 中的每个 &lt;Component&gt; 添加到 XML 1,但有两种不同的方式,具体取决于是否找到匹配的 &lt;Domain&gt;。你能举一个这个逻辑没有处理的例子吗?
    • 和你说的“join”有点不一样,因为我们这里是把XML2加到XML1上,把修改后的XML1作为结果。我们只需要处理 XML2(左/右连接,如果你愿意的话),因为 XML1 中的所有内容都已经存在(因为我们将使用 XML1 作为结果),所以在这种情况下不需要“完全外连接”设置 AFAICS
    • 你是对的,它有效。我试图解决的是,您的代码肯定会从其他文件中添加节点,但同一个文件中有节点,具有相同的域名。这也应该属于一种配置。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多