Союз с LINQ to XML - PullRequest
       41

Союз с LINQ to XML

5 голосов
/ 02 февраля 2009

Мне нужно объединить два набора элементов XE в один уникальный набор элементов. Используя метод расширения .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())
                   )
               );

Ответы [ 4 ]

4 голосов
/ 03 февраля 2009

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

0 голосов
/ 03 февраля 2009

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

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 голосов
/ 03 февраля 2009

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

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());
0 голосов
/ 02 февраля 2009

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

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

...