【问题标题】:Compare two xml and print the difference using LINQ比较两个 xml 并使用 LINQ 打印差异
【发布时间】:2009-09-24 06:12:04
【问题描述】:

我正在比较两个 xml,我必须打印差异。如何使用 LINQ 实现这一点。 我知道我可以使用 Microsoft 的 XML diff patch,但我更喜欢使用 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>

//第二个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>

我想比较这两个xml并像这样打印结果。

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

【问题讨论】:

  • xml 有多复杂?如果它是 just root/record/@attrib 它可能是可行的。
  • 嗨,Marc,这是一个非常简单的例子,实际上 xml 有点复杂。
  • 只是值和/或属性的不同还是结构也可以不同?
  • 仅在属性值上有所不同(如您所见)。结构永不改变。
  • 因此,只要两行中至少一个属性相同,它就应该将其他两个属性报告为不同?

标签: c# linq linq-to-xml compare


【解决方案1】:

解决办法如下:

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

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

【讨论】:

  • 如果这真的像要求的那样工作,我会感到惊讶你真的想要交叉连接(笛卡尔积)吗?
  • 是的,它有效。下次你可以自己检查一下,然后再发表评论。现在让我们“审查”您的解决方案。
  • 它对这个示例集产生相同的结果,是的。但它并没有解决我理解的一般问题。例如,假设 xml2 的 id=20508 的书是错字,并且下一个条目在每个源中都有“真实的”20508 数据。您的解决方案将返回两行;我的会返回一个。两个正确答案都取决于问题。
  • 嗨 Grega 如果我们有一本书缺少第一个 xml 或第二个 xml,我必须在上面的代码中添加哪些行才能工作。
  • 你的意思是如果一本书只在一个xml中而不在另一个?你希望发生什么?
【解决方案2】:

为了好玩,格雷格阅读问题的一般解决方案。为了说明我对这种方法的反对意见,我为“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;

报告如下:

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

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

【讨论】:

  • @grega g 如何在有 50 个值对时发现属性值的差异,我们是否需要按每个值对的属性名称进行比较,或者是否有更好的方法来做同样的事情。谢谢你..
【解决方案3】:

您在此处需要的操作是一个 Zip,用于配对您的两个书籍序列中的相应元素。该运算符是added in .NET 4.0,但我们可以通过使用 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 } }

【讨论】:

  • 那么 zip 的作用在于它将 xml1 的第一条记录连接到 xml2 的第一条记录。因此,如果我们稍微混合 xml1 - 假设我们切换第一个和第二个 节点 - 我们会得到不同的结果。这就是为什么你需要交叉连接。没有理由假设(从他的问题和 cmets)应该只比较相应的节点。
  • 这个问题被描述为一个差异。在差异中,顺序很重要。
猜你喜欢
  • 2019-05-15
  • 2019-08-03
  • 2014-11-01
  • 2013-06-17
  • 2016-12-24
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 2020-10-03
相关资源
最近更新 更多