Union mit LINQ to XML
-
21-08-2019 - |
Frage
Ich muß Union zwei Sätze von XElements in einen einzigen, einzigartigen Satz von Elementen. Mit Hilfe der .Union () Erweiterungsmethode, bekomme ich nur eine „union all“ statt eine Gewerkschaft. Bin ich etwas fehlt?
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())
)
);
Lösung
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, wird er sich durch Bezugnahme vergleichen . Zum Glück, können Sie es sagen können verschiedene Kriterien zu verwenden, indem Angabe eines IEqualityComparer
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.›;
}
}
Hinweis: Ich habe nicht den Rahmen zu Hause habe, so der genaue Code nicht getestet und der IEqualityComparer-Code ist von hier (scrollen auf den zweiten Pfosten nach unten).
Andere Tipps
Es ist wirklich hart, um Ihre „LEFT JOIN“ Beobachtung zu beheben, ohne zu sehen, was Sie zu diesem Schluss zu kommen, verwenden. Hier ist mein Schuss 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 (das ist, was LINQ to XML ist wirklich), Union gegen Referenztypen verwendet Verweisgleichheit auf Dubletten zu prüfen. XElement ist ein Referenztyp.
Ich war in der Lage den folgenden zur Arbeit zu 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())
)
);
Es muss doch einen besseren Weg geben.
Was ist so etwas wie das?
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());