2009-09-24 18 views
8

Estoy comparando dos xml y tengo que imprimir la diferencia. ¿Cómo puedo lograr esto usando LINQ? Sé que puedo usar el parche XML diff de Microsoft, pero prefiero usar LINQ. Si usted tiene alguna otra idea voy a poner en práctica esaCompara dos xml e imprime la diferencia usando LINQ

// Primero XML

<Books> 
<book> 
    <id="20504" image="C01" name="C# in Depth"> 
</book> 
<book> 
    <id="20505" image="C02" name="ASP.NET"> 
</book> 
<book> 
    <id="20506" image="C03" name="LINQ in Action "> 
</book> 
<book> 
    <id="20507" image="C04" name="Architecting Applications"> 
</book> 
</Books> 

// Segundo XML

<Books> 
    <book> 
    <id="20504" image="C011" name="C# in Depth"> 
    </book> 
    <book> 
    <id="20505" image="C02" name="ASP.NET 2.0"> 
    </book> 
    <book> 
    <id="20506" image="C03" name="LINQ in Action "> 
    </book> 
    <book> 
    <id="20508" image="C04" name="Architecting Applications"> 
    </book> 
</Books> 

quiero comparar estos dos xml e imprimir resultado como este.

Issued  Issue Type    IssueInFirst IssueInSecond 

1   image is different  C01    C011 
2   name is different  ASP.NET   ASP.NET 2.0 
3   id is different  20507   20508 
+4

¿Qué tan complejo es el xml? Si es * solo * root/record/@ attrib, probablemente sea posible. –

+1

(el xml no es válido, por cierto) –

+0

Hola Marc es un ejemplo muy simple en reall xml es un poco complejo. – NETQuestion

Respuesta

1

Aquí está la solución:

//sanitised xmls: 
string s1 = @"<Books> 
       <book id='20504' image='C01' name='C# in Depth'/> 
       <book id='20505' image='C02' name='ASP.NET'/> 
       <book id='20506' image='C03' name='LINQ in Action '/> 
       <book id='20507' image='C04' name='Architecting Applications'/> 
       </Books>"; 
string s2 = @"<Books> 
        <book id='20504' image='C011' name='C# in Depth'/> 
        <book id='20505' image='C02' name='ASP.NET 2.0'/> 
        <book id='20506' image='C03' name='LINQ in Action '/> 
        <book id='20508' image='C04' name='Architecting Applications'/> 
       </Books>"; 

XDocument xml1 = XDocument.Parse(s1); 
XDocument xml2 = XDocument.Parse(s2); 

//get cartesian product (i think) 
var result1 = from xmlBooks1 in xml1.Descendants("book") 
       from xmlBooks2 in xml2.Descendants("book") 
       select new { 
          book1 = new { 
             id=xmlBooks1.Attribute("id").Value, 
             image=xmlBooks1.Attribute("image").Value, 
             name=xmlBooks1.Attribute("name").Value 
             }, 
          book2 = new { 
             id=xmlBooks2.Attribute("id").Value, 
             image=xmlBooks2.Attribute("image").Value, 
             name=xmlBooks2.Attribute("name").Value 
             } 
          }; 

//get every record that has at least one attribute the same, but not all 
var result2 = from i in result1 
       where (i.book1.id == i.book2.id 
         || i.book1.image == i.book2.image 
         || i.book1.name == i.book2.name) && 
         !(i.book1.id == i.book2.id 
         && i.book1.image == i.book2.image 
         && i.book1.name == i.book2.name) 
       select i; 



foreach (var aa in result2) 
{ 
    //you do the output :D 
} 

Ambas declaraciones LINQ, probablemente podrían fusionarse, pero lo dejo como ejercicio para usted.

+0

Me sorprendería si esto realmente funciona como se solicita. ¿De verdad quieres una combinación cruzada (producto cartesiano)? – dahlbyk

+0

Sí, funciona. La próxima vez puede verificarlo usted mismo, antes de comentar.Ahora vamos a 'revisar' tu solución. –

+0

Produce el mismo resultado para este conjunto de ejemplos, sí. Pero no resuelve el problema general tal como lo entiendo. Por ejemplo, supongamos que el libro de xml2 con id = 20508 es un error tipográfico y la siguiente entrada tiene los datos "reales" 20508 en cada fuente. Su solución devolvería dos filas; el mío devolvería uno. Ambas respuestas correctas dependiendo de la pregunta. – dahlbyk

1

La operación que desea aquí es un Zip para emparejar los elementos correspondientes en sus dos secuencias de libros. Que el operador está siendo added in .NET 4.0, pero se puede fingir utilizando Seleccionar para agarrar los índices de los libros y la unión en que:

var res = from b1 in xml1.Descendants("book") 
         .Select((b, i) => new { b, i }) 
      join b2 in xml2.Descendants("book") 
         .Select((b, i) => new { b, i }) 
      on b1.i equals b2.i 

Vamos a continuación, utilizar una segunda unen para comparar los valores de atributos por su nombre. Tenga en cuenta que esta es una unión interna; si quisiera incluir atributos que faltan de uno u otro, tendría que trabajar un poco más.

  select new 
      { 
       Row = b1.i, 
       Diff = from a1 in b1.b.Attributes() 
        join a2 in b2.b.Attributes() 
         on a1.Name equals a2.Name 
        where a1.Value != a2.Value 
        select new 
        { 
         Name = a1.Name, 
         Value1 = a1.Value, 
         Value2 = a2.Value 
        } 
      }; 

El resultado será una colección anidada:

foreach (var b in res) 
{ 
    Console.WriteLine("Row {0}: ", b.Row); 
    foreach (var d in b.Diff) 
     Console.WriteLine(d); 
} 

o para obtener varias filas por libro:

var report = from r in res 
      from d in r.Diff 
      select new { r.Row, Diff = d }; 

foreach (var d in report) 
    Console.WriteLine(d); 

cual informa de lo siguiente:

{ Row = 0, Diff = { Name = image, Value1 = C01, Value2 = C011 } } 
{ Row = 1, Diff = { Name = name, Value1 = ASP.NET, Value2 = ASP.NET 2.0 } } 
{ Row = 3, Diff = { Name = id, Value1 = 20507, Value2 = 20508 } } 
+0

Bueno, la cosa con zip es que une el primer registro de xml1 al primer registro de xml2. Entonces, si mezclamos xml1 un poco - digamos que cambiamos el primer y segundo nodo - obtenemos un resultado diferente. Es por eso que necesitas una unión cruzada. No hay ninguna razón para suponer (a partir de sus preguntas y comentarios) que solo se deben comparar los nodos correspondientes. –

+0

La pregunta se describió como una diferencia. En una diferencia, el orden importa. – dahlbyk

1

Para diversión, una solución general para gre Gag lee el problema. Para ilustrar mi objeción a este enfoque, introduje una entrada "correcta" para "PowerShell en acción".

string s1 = @"<Books> 
    <book id='20504' image='C01' name='C# in Depth'/> 
    <book id='20505' image='C02' name='ASP.NET'/> 
    <book id='20506' image='C03' name='LINQ in Action '/> 
    <book id='20507' image='C04' name='Architecting Applications'/> 
    <book id='20508' image='C05' name='PowerShell in Action'/> 
    </Books>"; 
string s2 = @"<Books> 
    <book id='20504' image='C011' name='C# in Depth'/> 
    <book id='20505' image='C02' name='ASP.NET 2.0'/> 
    <book id='20506' image='C03' name='LINQ in Action '/> 
    <book id='20508' image='C04' name='Architecting Applications'/> 
    <book id='20508' image='C05' name='PowerShell in Action'/> 
    </Books>"; 

XDocument xml1 = XDocument.Parse(s1); 
XDocument xml2 = XDocument.Parse(s2); 

var res = from b1 in xml1.Descendants("book") 
      from b2 in xml2.Descendants("book") 
      let issues = from a1 in b1.Attributes() 
         join a2 in b2.Attributes() 
         on a1.Name equals a2.Name 
         select new 
         { 
          Name = a1.Name, 
          Value1 = a1.Value, 
          Value2 = a2.Value 
         } 
      where issues.Any(i => i.Value1 == i.Value2) 
      from issue in issues 
      where issue.Value1 != issue.Value2 
      select issue; 

cual informa de lo siguiente:

{ Name = image, Value1 = C01, Value2 = C011 } 
{ Name = name, Value1 = ASP.NET, Value2 = ASP.NET 2.0 } 
{ Name = id, Value1 = 20507, Value2 = 20508 } 
{ Name = image, Value1 = C05, Value2 = C04 } 
{ Name = name, Value1 = PowerShell in Action, Value2 = Architecting Applications } 

Tenga en cuenta que las dos últimas entradas son el "conflicto" entre el 20508 y el error tipográfico de otra forma correcta 20508 entrada.

Cuestiones relacionadas