我需要将两组 XElement 合并为一组唯一的元素。使用
.Union()
扩展方法,我只得到一个“union all”而不是联合。我是不是错过了什么?
var elements = xDocument.Descendants(w + "sdt")
.Union(otherDocument.Descendants(w + "sdt")
.Select(sdt =>
new XElement(
sdt.Element(w + "sdtPr")
.Element(w + "tag")
.Attribute(w + "val").Value,
GetTextFromContentControl(sdt).Trim())
)
);
您的第一个冲动几乎是正确的。:) 根据 David B,如果您没有准确地告诉 LINQ 如何定义相等性,然后给它一堆 XElement,它会通过引用来比较它们。幸运的是,您可以通过指定
IEqualityComparer‹XElement›
(基本上,一个具有 Equals 方法的对象,当两个 XElement 根据您的定义相等时返回 true,否则返回 false,以及一个接受GetHashCode
并根据您的平等标准返回哈希码)。例如:
XElement
...
项目中的其他地方
var elements = xDocument.Descendants(w + "sdt")
.Union(otherDocument.Descendants(w + "sdt", new XElementComparer())
.RestOfYourCode
注意:我家里没有框架,因此未测试确切的代码,IEqualityComparer 代码来自here
public class XElementComparer : IEqualityComparer‹XElement› {
public bool Equals(XElement x, XElement y) {
return ‹X and Y are equal according to your standards›;
}
public int GetHashCode(XElement obj) {
return ‹hash code based on whatever parameters you used to determine
Equals. For example, if you determine equality based on the ID
attribute, return the hash code of the ID attribute.›;
}
}
在 linq to 对象(这就是 linq to xml 的真正含义)中,针对引用类型的 Union 使用引用相等性来测试重复项。 XElement 是引用类型。
XDocument doc1 = XDocument.Parse(@"<XML><A/><C/></XML>");
XDocument doc2 = XDocument.Parse(@"<XML><B/><C/></XML>");
//
var query1 = doc1.Descendants().Union(doc2.Descendants());
Console.WriteLine(query1.Count());
foreach (XElement e in query1) Console.WriteLine("--{0}",e.Name);
6
--XML
--A
--C
--XML
--B
--C
//
var query2 = doc1.Descendants().Concat(doc2.Descendants())
.GroupBy(x => x.Name)
.Select(g => g.First());
Console.WriteLine(query2.Count());
foreach (XElement e in query2) Console.WriteLine("--{0}", e.Name);
4
--XML
--A
--C
--B
当然一定有更好的方法。
var elements = xDocument.Descendants(w + "sdt")
.Concat(otherDocument.Descendants(w + "sdt")
.Where(e => !xDocument.Descendants(w + "sdt")
.Any(x => x.Element(w + "sdtPr")
.Element(w + "tag")
.Attribute(w + "val").Value ==
e.Element(w + "sdtPr")
.Element(w + "tag")
.Attribute(w + "val").Value)))
.Select(sdt =>
new XElement(
sdt.Element(w + "sdtPr")
.Element(w + "tag")
.Attribute(w + "val").Value,
GetTextFromContentControl(sdt).Trim())
)
);