Pregunta

Necesito unir dos conjuntos de XElements en un único conjunto único de elementos. Usando el método de extensión .Union (), acabo de obtener una & Quot; union all & Quot; en lugar de una unión. ¿Me estoy perdiendo algo?

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())
                   )
               );
¿Fue útil?

Solución

Su primer impulso fue casi correcto. :) Según David B , si no le dice a LINQ exactamente cómo define la igualdad y luego le da un montón de elementos X, los comparará por referencia . Afortunadamente, puede indicarle que use diferentes criterios especificando un IEqualityComparer & # 8249; XElement & # 8250; (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 otro lugar de tu 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 casa, por lo que el código exacto no se prueba y el código IEqualityComparer es de aquí (desplácese hacia abajo hasta la segunda publicación).

Otros consejos

Es realmente difícil resolver los problemas de su " left join " observación sin ver qué es lo que está utilizando para llegar a esa conclusión. Aquí está mi tiro 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 to objects (que es lo que realmente es linq to xml), Unión contra tipos de referencia utiliza la igualdad de referencia para comprobar si hay duplicados. XElement es un tipo de referencia.

Pude hacer que funcionara lo siguiente, 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.

¿Qué pasa con algo como esto?

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());
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top