c# - 使用linq合并具有相同结构的多个XML文件并根据键删除重复项

标签 c# xml linq xslt merge

我有多个 XML 文件,我正试图合并到一个文件中。 Linq to XML 可能是最好的选择,但我对想法持开放态度(XSLT 似乎擅长合并两个文件,但在 n > 2 或 n = 大的情况下很笨拙)。

从这里阅读其他问题,某种连接看起来不错。

文件1.xml:

<first>
  <second>
    <third id="Id1">
      <values>
        <value a="1" b="one"/>
        <value a="2" b="two"/>
        <value a="3" b="three"/>
      </values>
    </third>
    <third id="Id2">
      <values>
        <value a="f" b="foo"/>
        <value a="b" b="bar"/>
        <value a="w" b="wibble"/>
      </values>
    </third>
  </second>
</first>

文件2.xml:

<first>
  <second>
    <third id="Id1">
      <values>
        <value a="2" b="two"/>
        <value a="3" b="three"/>
        <value a="6" b="six"/>
      </values>
    </third>
    <third id="Id3">
      <values>
        <value a="x" b="ex"/>
        <value a="y" b="why"/>
        <value a="z" b="zed"/>
      </values>
    </third>
  </second>
</first>

合并.xml:

<first>
  <second>
    <third id="Id1">
      <values>
        <value a="1" b="one"/>
        <value a="2" b="two"/>
        <value a="3" b="three"/>
        <value a="6" b="six"/>
      </values>
    </third>
    <third id="Id2">
      <values>
        <value a="f" b="foo"/>
        <value a="b" b="bar"/>
        <value a="w" b="wibble"/>
      </values>
    </third>
    <third id="Id3">
      <values>
        <value a="x" b="ex"/>
        <value a="y" b="why"/>
        <value a="z" b="zed"/>
      </values>
    </third>
  </second>
</first>

即它根据第三个/@id 属性合并值。

我如何使用 linq 优雅地做到这一点?

最佳答案

下面仍然很丑陋,我相信可以通过一些工作将它变成更流线型的形状,但现在这似乎可以完成工作:

public static void MergeXml()
{
    var xdoc1 = XDocument.Load(@"c:\temp\test.xml");
    var xdoc2 = XDocument.Load(@"c:\temp\test2.xml");
    var d1Targets = xdoc1.Descendants("third");
    var d2Selection = xdoc2.Descendants("third").ToList();

    Func<XElement, XElement, string, bool> attributeMatches = (x, y, a) =>
        x.Attribute(a).Value == y.Attribute(a).Value;

    Func<IEnumerable<XElement>, XElement, bool> hasMatchingValue = (ys, x) =>
        // remove && if matching "a" should cause replacement.
        ys.Any(d => attributeMatches(d, x, "a") && attributeMatches(d, x, "b"));

    foreach (var e in d1Targets)
    {
        var fromD2 = d2Selection.Find(x => attributeMatches(x, e, "id"));
        if (fromD2 != null)
        {
            d2Selection.Remove(fromD2);
            var dest = e.Descendants("value");
            dest.LastOrDefault()
                .AddAfterSelf(fromD2.Descendants("value").Where(x => !hasMatchingValue(dest, x)));
        }
    };
    if (d2Selection.Count > 0)
        d1Targets.LastOrDefault().AddAfterSelf(d2Selection);

    xdoc1.Save(@"c:\temp\merged.xml");
}

这会从 OPs 问题中的两个示例输入文件生成以下输出文件:

<?xml version="1.0" encoding="utf-8"?>
<first>
  <second>
    <third id="Id1">
      <values>
        <value a="1" b="one" />
        <value a="2" b="two" />
        <value a="3" b="three" />
        <value a="6" b="six" />
      </values>
    </third>
    <third id="Id2">
      <values>
        <value a="f" b="foo" />
        <value a="b" b="bar" />
        <value a="w" b="wibble" />
      </values>
    </third>
    <third id="Id3">
      <values>
        <value a="x" b="ex" />
        <value a="y" b="why" />
        <value a="z" b="zed" />
      </values>
    </third>
  </second>
</first>

关于c# - 使用linq合并具有相同结构的多个XML文件并根据键删除重复项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29802221/

相关文章:

c++ - RapidXML,读取和保存值

c# - 更改要导入到 Access 数据库的 Xml 的结构

linq - 如何在另一个表达式中创建表达式?

c# - 非重复计数 x 和按日期 y 分组

c# - 如何正确使用 LINQ 和 MySQL?

c# - Windows Phone 8(WP8) C# 不安全代码?

c# - 将 T 限制为 int 值?

php - SimpleXML 获取子元素之间的元素内容

c# - 从两个表中获取不同记录的 Linq 查询

c# - 如何在一种方法上编写不同案例的断言测试,并且在与不同的响应案例进行比较时,它们都通过了?