2009-02-02 9 views
5

Necesito unir dos conjuntos de XElements en un único conjunto único de elementos. Usando el método de extensión .Union(), acabo de obtener un "sindicato de todos" en lugar de una unión. ¿Me estoy perdiendo de algo?Unión con LINQ a 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()) 
        ) 
       ); 

Respuesta

4

Su primer impulso fue casi correcta. :) Según David B, si usted no dice LINQ exactamente cómo se define la igualdad y luego le da un montón de XElements, comparará por referencia. Afortunadamente, puede indicarle que use diferentes criterios especificando un IEqualityComparer (básicamente, un objeto que tiene un método Equals que devuelve verdadero si dos XElements son iguales de acuerdo con su definición y falso de lo contrario, y un método GetHashCode que toma un XElement y devuelve un código hash basado en sus criterios de igualdad).

Por ejemplo:

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

...

En algún otro lugar en su proyecto

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: no tengo el marco en el hogar, por lo que el código no es exacta probado y el código IEqualityComparer es de here (desplácese hacia abajo a la segunda publicación).

+0

Esto fue perfecto. ¡Gracias! –

0

Es realmente difícil de solucionar los problemas de la observación "izquierda unirse a" sin ver qué es lo que está utilizando para llegar a esa conclusión. Aquí está mi oportunidad en la oscuridad.

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 

en LINQ a objetos (que es lo que LINQ to XML es realmente), la Unión frente a los tipos de referencia utilizan la igualdad de referencia para la prueba de los duplicados. XElement es un tipo de referencia.

+0

Resulta que estaba equivocado acerca de mi problema de "me fui a la izquierda". Esa fue mi culpa Sin embargo, el operador de la unión devuelve una "unión de todo" (editó mi pregunta original para reflejar esto). Voy a probar tu solución. –

+0

Esto no funciona para mí, ya que la diferencia en los elementos es el valor del atributo de los elementos de nieto (ver mi respuesta a continuación). –

0

que era capaz de conseguir lo siguiente para el trabajo, pero es bastante feo:

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

Seguramente debe haber una mejor manera.

0

¿Qué tal algo así?

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