2009-02-02 1 views
5

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

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

ответ

4

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

+0

Это было прекрасно. Благодаря! – 2009-02-03 15:29:53

0

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

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 к объектам (что LINQ к XML на самом деле), Союз против ссылочных типов использует равенство ссылок для проверки дубликатов. XElement является ссылочным типом.

+0

Оказывается, я ошибался в вопросе о «левом соединении». Это была моя вина. Тем не менее, оператор Союза возвращает «union all» (отредактировал мой оригинальный вопрос, чтобы отразить это). Я попробую ваше решение. – 2009-02-02 20:27:27

+0

Это не работает для меня, так как разница в элементах - это значение атрибута элементов внука (см. Мой ответ ниже). – 2009-02-02 21:38:31

0

Я был в состоянии получить следующую за работу, но это довольно некрасиво:

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

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

0

Как насчет этого?

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