2009-02-02 17 views
5

Ho bisogno di unire due set di XElements in un unico insieme di elementi univoci. Usando il metodo di estensione .Union(), ottengo solo un "union all" invece di un union. Mi sto perdendo qualcosa?Unione con LINQ to 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()) 
        ) 
       ); 

risposta

4

Il tuo primo impulso fu quasi corretta. :) Come per David B, se non dite LINQ esattamente come si definisce l'uguaglianza e poi dargli un po 'di XElements, sarà confrontarli per riferimento. Fortunatamente, puoi dire di usare criteri diversi specificando un IEqualityComparer (in pratica, un oggetto che ha un metodo Equals che restituisce true iff due XElements sono uguali secondo la tua 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 

...

qualche altra parte nel vostro 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 quadro in casa, in modo che il codice esatto non è testato e il codice IEqualityComparer è da here (scorrere verso il basso al secondo post).

+0

Questo è stato perfetto. Grazie! –

0

E 'davvero difficile per risolvere i problemi di osservazione "sinistra join" senza vedere che cosa è che si sta utilizzando per venire a questa conclusione. Ecco il mio colpo 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 to Objects (che è ciò che LINQ to XML è davvero), dell'Unione contro i tipi di riferimento utilizza l'uguaglianza di riferimento per verificare i duplicati. XElement è un tipo di riferimento.

+0

Si è verificato un errore nel mio problema "left join". È stata colpa mia Tuttavia, l'operatore dell'Unione restituisce un "unione tutti" (modifica la mia domanda originale per riflettere questo). Proverò la tua soluzione. –

+0

Questo non funziona per me in quanto la differenza tra gli elementi è il valore dell'attributo degli elementi grandchild (vedi la mia risposta sotto). –

0

sono stato in grado di ottenere quanto segue per lavoro, 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.

0

E qualcosa di simile?

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