2009-09-24 9 views
8

私は2つのxmlを比較していますが、違いを表示する必要があります。どのようにLINQを使ってこれを達成できますか? 私はMicrosoftがXML差分パッチを使用できることを知っていますが、私はLINQを使うことを好みます。あなたが他のアイデアを持っている場合は、私はこのように、この2つのXMLおよび印刷結果を比較したい2つのxmlを比較し、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> 

// 2番目の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> 

ことを実施していきます。ここで

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

xmlはどのくらい複雑ですか?それが*単に* root/record/@ attribならばおそらく実行可能です。 –

+1

(xmlは無効です、btw) –

+0

こんにちはMarcこれは非常に単純な例です。 – NETQuestion

答えて

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文は、おそらくマージすることができ、私はあなたのための練習としてそのままにしておきます。

+0

これが実際に要求されたように機能するのであれば、私は驚くでしょう。あなたは本当にクロス結合(デカルト積)を望んでいますか? – dahlbyk

+0

それはうまく動作します。次回はコメントをする前に自分で確認することができます。今すぐあなたの解決策を「レビュー」しましょう。 –

+0

この例のセットでは、同じ結果が生成されます。しかし、私が理解しているように、それは一般的な問題を解決しません。たとえば、id = 20508のxml2の本がタイプミスであり、次のエントリが各ソースに「実際の」20508データを持っていたとします。あなたのソリューションは2行を返します。私のものは1つを返すでしょう。両方の質問に応じて正しい答え。 – dahlbyk

1

ここで必要な操作は、2つの一連の書籍の対応する要素をペアにするZipです。そのオペレータはadded in .NET 4.0されているが、我々は、本インデックスをつかむために選択を使用して、その上で参加することによって、それ偽物できます。

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 

私たちは、その後、第二を使用しますが、名前で属性の値を比較することで参加します。これは内部結合であることに注意してください。あなたが1つまたは他のものから欠落している属性を含めることを望むなら、あなたはかなり多くの作業をする必要があります。

  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 } } 
+0

さて、zipのことは、最初のレコードをxml1からxml2の最初のレコードに結合することです。だからxml1を少し混ぜると、最初と2番目のノードを切り替えることができます。ノードは別の結果になります。そのため、クロス・ジョインが必要です。彼の質問とコメントから、対応するノードだけを比較すべきだと仮定する理由はない。 –

+0

質問はdiffとして説明されました。 diffでは、順序は重要です。 – dahlbyk

1

の場合を楽しい、greの一般的な解決策問題の解読このアプローチへの私の反対を説明するために、私は「PowerShell in Action」の「正しい」エントリを導入しました。最後の2つのエントリは、20508タイプミスとそうでない場合は、正しい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 } 

注:以下の報告

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; 

関連する問題