与 LINQ to XML 联合

发布于 2024-07-12 07:16:44 字数 577 浏览 5 评论 0原文

我需要将两组 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())
                   )
               );

I need to union two sets of XElements into a single, unique set of elements. Using the .Union() extension method, I just get a "union all" instead of a union. Am I missing something?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

春庭雪 2024-07-19 07:16:44

您的第一反应几乎是正确的。:) 根据 David B,如果您没有准确地告诉 LINQ 如何定义相等性,然后给它一堆 XElement,它会通过引用来比较它们。 幸运的是,您可以通过指定 IEqualityComparer 来告诉它使用不同的条件(基本上,一个具有 Equals 方法的对象,当两个 XElement 根据您的定义相等时返回 true,否则返回 false,并且GetHashCode 方法,该方法采用 XElement 并根据您的相等标准返回哈希代码)。

例如:

var elements = xDocument.Descendants(w + "sdt")
               .Union(otherDocument.Descendants(w + "sdt", new XElementComparer())
               .RestOfYourCode

...

项目中的其他地方

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.›;

 }

 }

注意:我家里没有框架,因此未测试确切的代码,IEqualityComparer 代码来自 此处(向下滚动到第二篇文章)。

Your first impulse was almost correct.:) As per David B, if you do not tell LINQ exactly how you define equality and then give it a bunch of XElements, it will compare them by reference. Fortunately, you can tell it to use different criteria by specifying an IEqualityComparer‹XElement› (basically, an object that has an Equals method that returns true iff two XElements are equal according to your definition and false otherwise and a GetHashCode method that takes an XElement and returns a hash code based on your equality criteria).

For example:

var elements = xDocument.Descendants(w + "sdt")
               .Union(otherDocument.Descendants(w + "sdt", new XElementComparer())
               .RestOfYourCode

...

Somewhere else in your project

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.›;

 }

 }

Note: I do not have the framework at home, so the exact code is not tested and the IEqualityComparer code is from here (scroll down to second post).

胡大本事 2024-07-19 07:16:44

如果不知道您使用什么来得出该结论,那么真的很难对您的“左连接”观察进行故障排除。 这是我在黑暗中拍摄的照片。

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

在 linq to 对象(这就是 linq to xml 的真正含义)中,针对引用类型的 Union 使用引用相等性来测试重复项。 XElement 是引用类型。

It's really hard to troubleshoot your "left join" observation without seeing what it is you are using to come to that conclusion. Here's my shot in the dark.

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

In linq to objects (which is what linq to xml really is), Union against reference types uses reference equality to test for duplicates. XElement is a reference type.

我们的影子 2024-07-19 07:16:44

我能够让以下工作发挥作用,但它非常丑陋:

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

当然必须有更好的方法。

I was able to get the following to work, but it is quite ugly:

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

Surely there must be a better way.

最舍不得你 2024-07-19 07:16:44

像这样的事情怎么办?

var xDoc = from f in xDocument.Descendants(w + "sdt")
    select new {xNode = f, MatchOn = f.Element(w + "sdtPr").Element(w + "tag").Attribute(w + "val").Value };

var oDoc = from o in otherDocument.Descendants(w + "sdt")
    select new {MatchOn = o.Element(w + "sdtPr").Element(w + "tag").Attribute(w + "val").Value };

var elements = from x in xDoc.Where(f => !oDoc.Any(o => o.MatchOn == f.MatchOn))
    select new XElement(x.MatchOn, GetTextFromContentControl(x.xNode).Trim());

What about something like this?

var xDoc = from f in xDocument.Descendants(w + "sdt")
    select new {xNode = f, MatchOn = f.Element(w + "sdtPr").Element(w + "tag").Attribute(w + "val").Value };

var oDoc = from o in otherDocument.Descendants(w + "sdt")
    select new {MatchOn = o.Element(w + "sdtPr").Element(w + "tag").Attribute(w + "val").Value };

var elements = from x in xDoc.Where(f => !oDoc.Any(o => o.MatchOn == f.MatchOn))
    select new XElement(x.MatchOn, GetTextFromContentControl(x.xNode).Trim());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文