繁体   English   中英

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

[英]How to compare two Json objects using C#

我有两个 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 进行比较,如下所示。 而用上面的比较方式让他们Pass是错误的。

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

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

我做了更多的挖掘,并能够找出为什么 OP 的测试代码没有按预期运行。 我能够通过安装和使用FluentAssertions.Json nuget 包来修复它。

一件重要的事情:

确保包含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);
    }
}

运行测试:

单元测试结果

考虑使用 Newtonsoft 提供的JToken.DeepEquals()方法。 无论您使用的是哪种测试框架,它都会看起来有点像这样:

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

制作了一个非递归方法,将删除双胞胎 - 想法是从非常相似的 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? (新的没有,所以当其中一个对象的顺序颠倒时它可能会非常慢 - 在解析过程中排序会更容易,或者至少将双胞胎的两个邻居作为第一个搜索步骤进行比较)

一种选择是将 json 字符串反序列化为 C# 对象并进行比较。

与使用JToken.DeepEquals (如JToken.DeepEquals所建议的)相比,这种方法需要更多的工作,但如果您的测试失败,则具有提供更好的错误消息的优点(请参见下面的屏幕截图)。

您的 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。 运行测试:

单元测试结果

我有两个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%>"  
}

我需要两个对象用于审计日志记录,我编写了如下代码。 它对我很有用。

https://github.com/wbish/jsondiffpatch.net

public static bool DeepCompare(this object obj, object another)
    {   
            var diffObj = new JsonDiffPatch();
        
            if (ReferenceEquals(obj, another)) return true;
            if ((obj == null) || (another == null)) return false;
            if (obj.GetType() != another.GetType()) return false;

            var objJson = JsonConvert.SerializeObject(obj);
            var anotherJson = JsonConvert.SerializeObject(another);
            var result = diffObj.Diff(objJson, anotherJson);
            return result == null;
    }

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM