Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 比较两个xml并使用LINQ打印差异_C#_Linq_Linq To Xml_Compare - Fatal编程技术网

C# 比较两个xml并使用LINQ打印差异

C# 比较两个xml并使用LINQ打印差异,c#,linq,linq-to-xml,compare,C#,Linq,Linq To Xml,Compare,我正在比较两个xml,我必须打印差异。如何使用LINQ实现这一点。 我知道我可以使用微软的XMLDiff补丁,但我更喜欢使用LINQ。如果你有任何其他想法,我会实施 //第一个Xml <Books> <book> <id="20504" image="C01" name="C# in Depth"> </book> <book> <id="20505" image="C02" name="ASP.NET"&

我正在比较两个xml,我必须打印差异。如何使用LINQ实现这一点。 我知道我可以使用微软的XMLDiff补丁,但我更喜欢使用LINQ。如果你有任何其他想法,我会实施

//第一个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>
以下是解决方案:

//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
}
//经过消毒的XML:
字符串s1=@”
";
字符串s2=@”
";
XDocument xml1=XDocument.Parse(s1);
XDocument xml2=XDocument.Parse(s2);
//得到笛卡尔积(我想)
var result1=来自xml1.subjects(“book”)中的xmlBooks1
来自xml2.substands中的xmlBooks2(“book”)
选择新{
book1=新{
id=xmlBooks1.Attribute(“id”).Value,
image=xmlBooks1.Attribute(“image”).Value,
name=xmlBooks1.Attribute(“name”).Value
}, 
book2=新{
id=xmlBooks2.Attribute(“id”).Value,
image=xmlBooks2.Attribute(“image”).Value,
name=xmlBooks2.Attribute(“name”).Value
} 
};
//使至少有一个属性的每个记录都相同,但不是全部
var result2=来自result1中的i
其中(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)
选择i;
foreach(结果2中的var aa)
{
//你做输出:D
}

这两个linq语句可能都可以合并,但我把它留给您作为练习。

这里您需要的操作是一个Zip,将两本书序列中的对应元素配对。该操作符正在运行,但我们可以通过使用Select获取图书索引并加入其中来伪造它:

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
然后,我们将使用第二个联接按名称比较属性的值。请注意,这是一个内部连接;如果您确实想包含其中一个缺少的属性,那么您需要做更多的工作

          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
                     }
          };
结果将是一个嵌套集合:

foreach (var b in res)
{
    Console.WriteLine("Row {0}: ", b.Row);
    foreach (var d in b.Diff)
        Console.WriteLine(d);
}
或要获取每本书的多行:

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);
报告如下:

{ 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 } }
{ 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 }

为了好玩,我们提供了格雷加·g对这个问题的解读的一般解决方案。为了说明我对这种方法的反对,我为“PowerShell in Action”引入了一个“正确”的条目

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;

请注意,最后两个条目是20508打字错误与正确的20508条目之间的“冲突”。

xml有多复杂?如果它仅仅是root/record/@attrib,那么它可能是可行的。您好,Marc,这是一个非常简单的示例,在实际的xml中,它有点复杂。差异只是在值和/或属性上,或者结构也可以不同吗?差异只是在属性值上(如您所见)。结构永远不会改变。因此,当两行中至少有一个属性相同时,它应该报告其他两个属性不同?如果这真的像请求的那样工作,我会感到惊讶。你真的想要交叉连接(笛卡尔积)?是的,它工作。下次你可以在评论之前自己检查一下。现在让我们“回顾”您的解决方案。它会为这个示例集生成相同的结果,是的。但这并不能解决我所理解的一般问题。例如,假设xml2的id=20508的书是一个打字错误,下一个条目的每个源中都有“真实的”20508数据。您的解决方案将返回两行;我的将返回一个。根据问题的不同,这两个答案都是正确的。嗨,Grega,如果我们有一本书缺少第一个xml或第二个xml,我必须在上面的代码中添加哪些行才能工作呢?你的意思是如果一本书只在一个xml中,而不在另一个xml中?您希望发生什么呢?zip的问题是它将xml1的第一条记录连接到xml2的第一条记录。所以,如果我们稍微混合xml1,比如说我们切换第一个和第二个节点,我们会得到不同的结果。这就是为什么你需要交叉连接。没有理由(根据他的问题和评论)认为只应比较对应的节点。该问题被描述为差异。在差异中,顺序很重要。@grega g当有50个值对时,如何发现属性值的差异,我们是否需要按每个属性的名称进行比较,还是有更好的方法进行比较。非常感谢。
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;
{ 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 }