2009-02-02 6 views
5

Ich muss zwei Sätze von XElements zu einem einzigen, einzigartigen Satz von Elementen verbinden. Mit der Erweiterungsmethode .Union() erhalte ich nur eine "Union all" anstelle einer Union. Fehle ich etwas?Union mit LINQ zu 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()) 
        ) 
       ); 

Antwort

4

Ihr erster Impuls war fast richtig. :) Wie pro David B, wenn Sie LINQ nicht genau sagen, wie Sie Gleichheit definieren und dann eine Reihe von XElements geben, es wird sie vergleichen Bezug genommen wird. Glücklicherweise können Sie verschiedene Kriterien angeben, indem Sie ein IEqualityComparer angeben (im Grunde ein Objekt mit einer Equals-Methode, die true zurückgibt, wenn zwei XElements entsprechend Ihrer Definition gleich sind und andernfalls false und eine GetHashCode-Methode, die ein XElement verwendet und gibt einen Hash-Code basierend auf Ihren Gleichheits-Kriterien zurück).

Zum Beispiel:

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

...

Irgendwo anders in Ihrem Projekt

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

} 

} 

Anmerkung: Ich habe nicht den Rahmen zu Hause, so dass der genaue Code nicht ist getestet und der IEqualityComparer-Code ist von here (scrollen Sie zum zweiten Post).

+0

Das war perfekt. Vielen Dank! –

0

Es ist wirklich hart, um Ihre „LEFT JOIN“ Beobachtung zu beheben, ohne zu sehen, was Sie zu diesem Schluss zu kommen, verwenden. Hier ist meine Aufnahme im Dunkeln.

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 zu Objekten (was LINQ to XML ist wirklich), verwendet Union gegen Referenztypen Referenz Gleichheit nach Duplikaten zu testen. XElement ist ein Referenztyp.

+0

Es stellte sich heraus, dass ich mich bei meinem Problem mit der "linken Verknüpfung" irrte. Das war meine Schuld. Der Unionsbetreiber gibt jedoch eine "Union all" zurück (meine ursprüngliche Frage wurde entsprechend bearbeitet). Ich werde deine Lösung versuchen. –

+0

Dies funktioniert nicht für mich, da der Unterschied in den Elementen der Attributwert der Enkelelemente ist (siehe meine Antwort unten). –

0

konnte ich folgendes zur Arbeit kommen, aber es ist ziemlich hässlich:

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

Sicherlich muss es einen besseren Weg geben.

0

Was ist mit so etwas?

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