Вопрос

Мне нужно объединить два набора XElements в единый, уникальный набор элементов.Используя .Метод расширения Union(), я просто получаю "объединить все" вместо объединения.Я что-то упускаю?

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())
                   )
               );
Это было полезно?

Решение

Ваш первый импульс был почти правильным.:) Согласно Дэвид Б, если вы не сообщите LINQ точно, как вы определяете равенство, а затем предоставите ему набор XElements , он будет сравнивать их по ссылке.К счастью, вы можете указать ему использовать другие критерии, указав IEqualityComparer"XElement" (в основном, объект, у которого есть метод Equals, который возвращает true, если два элемента XElement равны в соответствии с вашим определением, и 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 взят из здесь (прокрутите вниз до второго поста).

Другие советы

Действительно трудно устранить неполадки в вашем наблюдении за "левым соединением", не видя, что именно вы используете, чтобы прийти к такому выводу.Вот мой снимок в темноте.

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 objects (что на самом деле является linq to xml) Объединение против ссылочных типов использует равенство ссылок для проверки на наличие дубликатов.XElement - это ссылочный тип.

Мне удалось заставить работать следующее, но это довольно уродливо:

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

Конечно, должен быть лучший способ.

Как насчет чего-то подобного этому?

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());
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top