如何使用C#比较两个Json对象

时间:2018-10-04 11:30:21

标签: c# unit-testing json.net assertion fluent-assertions

我有两个Json对象,如下所示。我正在使用Newtonsoft库进行Json解析。

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进行如下比较。并使用上述比较方法使它们通过,这是错误的。

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

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

6 个答案:

答案 0 :(得分:6)

请考虑使用Newtonsoft提供的JToken.DeepEquals()方法。不管您使用哪种测试框架,它都看起来像这样:

Console.WriteLine(JToken.DeepEquals(InstanceObjActual, InstanceObjExpected));
// false

答案 1 :(得分:6)

我做了一些更深入的研究,并且能够找出为什么OP的测试代码未按预期运行。我可以通过安装和使用FluentAssertions.Json nuget软件包来修复它。

一件重要的事情:

  

请确保包含using FluentAssertions.Json,否则为false   肯定会发生。

测试代码如下:

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

答案 2 :(得分:1)

如果您正在寻找一个轻量级的库来比较两个JSON对象(或几乎任何可序列化的实体),则可以使用以下包,该包(当前)使用Newtonsoft.Json JObjects并根据简单约定突出显示差异(*修改,-删除,+从/添加到第二个操作数),如下所示。

JSON 1

{
  "name":"John",
  "age":30,
  "cars": {
    "car1":"Ford",
    "car2":"BMW",
    "car3":"Fiat"
  }
 }

JSON 2

{
  "name":"John",
  "cars": {
    "car1":"Ford",
    "car2":"BMW",
    "car3":"Audi",
    "car4":"Jaguar"
  }
 }

用法


 var j1 = JToken.Parse(Read(json1));
 var j2 = JToken.Parse(Read(json2));

 var diff = JsonDifferentiator.Differentiate(j1,j2);

结果

{
  "-age": 30,
  "*cars": {
    "*car3": "Fiat",
    "+car4": "Jaguar"
  }
}

https://www.nuget.org/packages/JsonDiffer

答案 3 :(得分:0)

将json反序列化为C#对象后,正确的方法是在反序列化类中实现IComparable接口并比较这两个对象。

所以:

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

答案 4 :(得分:0)

一种选择是将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

答案 5 :(得分:0)

正在研究一种非递归方法,该方法将删除双胞胎,但可用于两个小的相似测试JSON,但想法是从非常相似的JSON中删除相同的元素,以便每个对象中仅保留不同的节点:

public void RemoveDuplicates(ref BrowseNode v1, ref BrowseNode v2)
{
    BreadthFirst bf1 = new BreadthFirst(v1);
    BreadthFirst bf2 = new BreadthFirst(v2);
    BrowseNode traversal = bf1.Root;
    bf2.NextAs(bf1);
    Boolean removed = true;
    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(bf2);
            if (removed && bf1.Orphan && bf2.Orphan)
            {
                traversal = bf1.RemoveCurrent();
                bf2.RemoveCurrent();
                if (traversal == null) break;
            }
            else removed = false;
        } while (removed);
        if (!removed) traversal = bf1.Next();
    } while (traversal != null);
}

在我的GitHub(配置文件或以下)上完成代码+解析器。
较旧的CSV版本也可以对我在这里的问题中提到的输入进行排序How to compare big JSON's?(新的CSV版本不会,因此当其中一个对象的顺序颠倒时,它可能会非常慢-在解析过程中更容易排序,或者至少将两者进行比较双胞胎的邻居作为第一个搜索步骤)