Domanda

Ho bisogno di unire due set di XElement in un unico set unico di elementi. Usando il metodo di estensione .Union (), ottengo solo un & Quot; union all & Quot; invece di un'unione. Mi sto perdendo qualcosa?

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())
                   )
               );
È stato utile?

Soluzione

Il tuo primo impulso è stato quasi corretto. :) Secondo David B , se non dici a LINQ esattamente come definisci l'uguaglianza e poi gli dai un mucchio di XElementi, li confronterà per riferimento . Fortunatamente, puoi dirgli di usare criteri diversi specificando un IEqualityComparer & # 8249; XElement & # 8250; (fondamentalmente, un oggetto che ha un metodo Equals che restituisce true se due XElement sono uguali in base alla definizione e false altrimenti e un metodo GetHashCode che accetta un XElement e restituisce un codice hash basato sui criteri di uguaglianza).

Ad esempio:

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

...

Da qualche altra parte nel tuo progetto

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

 }

 }

Nota: non ho il framework a casa, quindi il codice esatto non è testato e il codice IEqualityComparer proviene da qui (scorri verso il basso fino al secondo post).

Altri suggerimenti

È davvero difficile risolvere il tuo " left join " osservazione senza vedere cosa stai usando per giungere a quella conclusione. Ecco il mio scatto al buio.

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 agli oggetti (che in realtà è linq in xml), l'unione contro i tipi di riferimento utilizza l'uguaglianza di riferimento per verificare la presenza di duplicati. XElement è un tipo di riferimento.

Sono stato in grado di far funzionare quanto segue, ma è abbastanza brutto:

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

Sicuramente ci deve essere un modo migliore.

Che ne dici di qualcosa del genere?

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());
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top