Unie met LINQ na XML
-
21-08-2019 - |
Vra
Ek moet unie twee stelle XElements in 'n enkele, unieke stel elemente. Die gebruik van die .Union () uitbreiding metode, kry ek net 'n "unie al" in plaas van 'n vakbond. Mis ek iets?
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())
)
);
Oplossing
Jou eerste impuls was byna korrek. :) Soos per David B , as jy nie LINQ vertel presies hoe jy gelykheid te definieer en gee dit dan 'n klomp van die XElements, dit sal hulle vergelyk word met verwysing . Gelukkig kan jy dit vertel aan verskillende kriteria gebruik deur die spesifiseer van 'n IEqualityComparer
Byvoorbeeld:
var elements = xDocument.Descendants(w + "sdt")
.Union(otherDocument.Descendants(w + "sdt", new XElementComparer())
.RestOfYourCode
...
Iewers anders in jou projek
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.›;
}
}
Let wel: Ek het nie die raamwerk van die huis af, so die presiese kode is nie getoets en die IEqualityComparer kode is uit hier (gaan na tweede post).
Ander wenke
Dit is regtig moeilik om jou "links te sluit" waarneming op te los sonder om te sien wat dit is wat jy gebruik om daardie gevolgtrekking te kom. Hier is my skoot in die donker.
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 om voorwerpe (en dit is wat linQ aan xml regtig is), Unie teen verwysing tipes gebruik verwysing gelykheid te toets vir duplikate. XElement is 'n verwysing tipe.
Ek was in staat om die volgende te werk te kry, maar dit is nogal lelik:
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())
)
);
Sekerlik moet daar 'n beter manier wees.
Wat van iets soos hierdie?
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());