web-dev-qa-db-ja.com

C#を使用して2つのJsonオブジェクトを比較する方法

以下の2つのJsonオブジェクトを比較する必要があります。 Json解析にNewtonsoftライブラリを使用しています。

string InstanceExpected = jsonExpected;
string InstanceActual = jsonActual;
var InstanceObjExpected = JObject.Parse(InstanceExpected);
var InstanceObjActual = JObject.Parse(InstanceActual);

また、Fluent Assertionsを使用して比較しています。ただし、問題は、属性カウント/名前が一致しない場合にのみFluentアサーションが失敗することです。 JSON値が異なる場合、合格します。値が異なる場合、失敗する必要があります。

InstanceObjActual.Should().BeEquivalentTo(InstanceObjExpected);

たとえば、以下のように比較する実際のjsonと予想されるjsonがあります。そして、上記の比較方法を使用して、それらを合格にしますが、これは間違っています。

{
  "Name": "20181004164456",
  "objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"  
}

{
  "Name": "AAAAAAAAAAAA",
  "objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"  
}
6
Nandakumar1712

私はもう少し掘り下げて、OPのテストコードが期待どおりに実行されない理由を見つけることができました。 FluentAssertions.Json nugetパッケージをインストールして使用することで修正できました。

1つの重要なこと:

必ずusing FluentAssertions.Jsonを含めてください。そうしないと、誤検知が発生する可能性があります。

テストコードは次のとおりです。

using FluentAssertions;
using FluentAssertions.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;

[TestFixture]
public class JsonTests
{
    [Test]
    public void JsonObject_ShouldBeEqualAsExpected()
    {
        JToken expected = JToken.Parse(@"{ ""Name"": ""20181004164456"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }");
        JToken actual = JToken.Parse(@"{ ""Name"": ""AAAAAAAAAAAA"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }");

        actual.Should().BeEquivalentTo(expected);
    }
}

テストの実行:

Unit test results

13
Rui Jarimba

Newtonsoftが提供するJToken.DeepEquals()メソッドの使用を検討してください。使用しているテストフレームワークに関係なく、次のようになります。

Console.WriteLine(JToken.DeepEquals(InstanceObjActual, InstanceObjExpected));
// false
12
Jesse de Wit

双子を削除する非再帰的メソッドを作成しました-非常に類似したJSONから同じ要素を削除し、各オブジェクトに異なるノードのみが残るようにすることを考えています:

public void RemoveTwins(ref BreadthFirst bf1, ref BreadthFirst bf2) {
    JsonNode traversal = bf1.Next();
    Boolean removed = false;
    do {
        if (!removed) {
            if (bf2.Current != null) while (bf1.Level == bf2.Level && bf2.Next() != null) ;
            if (bf2.Current != null) while (bf1.Level != bf2.Level && bf2.Next() != null) ;
            else bf2.Current = bf2.root;
        }
        else traversal = bf1.Next();
        if (bf2.Level < 0) bf2.Current = bf2.Root;
        do {
            removed = bf1.NextAs(bf1.src, bf2, bf2.src);
            if (removed && bf1.Orphan && bf2.Orphan) {
                JsonNode same = bf1.Current.Parent;
                traversal = bf1.RemoveCurrent();
                same = bf2.Current.Parent;
                bf2.RemoveCurrent();
                bf1.UpdateLevel();
                bf2.UpdateLevel();
                if (traversal == null
                || bf1.Root == null || bf2.Root == null
                || (bf1.Level == 0 && bf1.Current.NodeBelow == null)) {
                    traversal = null;
                    break;
                }
            } else
            if (!removed) {
                break; 
            } else removed = false;
        } while (removed);
        if (!removed) traversal = bf1.Next();
    } while (traversal != null);
}

GitHubで完全なコード+パーサー(プロファイル以下)。
ここで私の質問で言及された入力もソートする古いCSVバージョン 大きなJSONを比較する方法? (新しいものはそうではないため、オブジェクトの1つが順序を逆にすると非常に遅くなる可能性があります-解析中にソートするか、少なくとも最初の検索ステップとして双子の両方の隣人を比較する方が簡単です)

2
Tom

1つのオプションは、JSON文字列をC#オブジェクトに逆シリアル化し、それらを比較することです。

このアプローチは、JToken.DeepEquals(@JessedeWitが示唆する)を使用する場合と比べてより多くの作業が必要ですが、テストが失敗した場合により良いエラーメッセージを提供するという利点があります(下のスクリーンショットを参照)。

JSON文字列は、次のクラスにモデル化できます。

public class Entity
{
    [JsonProperty("Name")]
    public string Name { get; set; }

    [JsonProperty("objectId")]
    public string ObjectId { get; set; }
}

テストでは、json文字列をオブジェクトにデシリアライズし、それらを比較します。

[TestFixture]
public class JsonTests
{
    [Test]
    public void JsonString_ShouldBeEqualAsExpected()
    {
        string jsonExpected = @"{ ""Name"": ""20181004164456"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }";
        string jsonActual = @"{ ""Name"": ""AAAAAAAAAAAA"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }";

        Entity expectedObject = JsonConvert.DeserializeObject<Entity>(jsonExpected);
        Entity actualObject = JsonConvert.DeserializeObject<Entity>(jsonActual);

        actualObject.Should().BeEquivalentTo(expectedObject);
    }
}

PS:テストメソッドでNUnitとFluentAssertionsを使用しました。テストの実行:

Unit test results

0
Rui Jarimba

JSONをC#オブジェクトに逆シリアル化した後、正しい方法は、逆シリアル化されたクラスにIComparableインターフェイスを実装し、2つのオブジェクトを比較することです。

そう:

using System;
using System.Collections.Generic;

class MyObj : IComparable<MyObj>
{
    public string Name { get; set; }
    public string ObjectID { get; set; }

    public int CompareTo(MyObj other)
    {
        if ((this.Name.CompareTo(other.Name) == 0) &&
            (this.ObjectID.CompareTo(other.ObjectID) == 0))
        {
            return 0;
        }
        return -1;
    }
}
0
Itamar Kerbel