如何使用 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 来比较它。但问题是只有当属性 count/names 不匹配时,Fluent 断言才会失败。如果 json 值不同,则通过。当值不同时我要求失败。
InstanceObjActual.Should().BeEquivalentTo(InstanceObjExpected);
例如,我有实际的和预期的 json 进行比较,如下所示。而用上面的比较方式让他们通过是错误的。
{
"Name": "20181004164456",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}
{
"Name": "AAAAAAAAAAAA",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}
将 json 反序列化为 C# 对象后,正确的方法是在反序列化的 class 中实现 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;
}
}
一种选择是将 json 字符串反序列化为 C# 对象并进行比较。
与使用 JToken.DeepEquals
相比,这种方法需要做更多工作(如@JessedeWit 所建议),但它的优点是可以在测试失败时提供更好的错误消息(请参见下面的屏幕截图)。
您的 json 字符串可以建模为以下内容 class:
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。 运行 测试:
考虑使用 Newtonsoft 提供的 JToken.DeepEquals()
方法。无论您使用哪种测试框架,它看起来都像这样:
Console.WriteLine(JToken.DeepEquals(InstanceObjActual, InstanceObjExpected));
// false
我做了更多的挖掘,并且能够找出为什么 OP 的测试代码没有像预期的那样 运行。我能够通过安装和使用 FluentAssertions.Json nuget 包来修复它。
一件重要的事情:
Be sure to include using FluentAssertions.Json
otherwise false
positives may occur.
测试代码如下:
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);
}
}
运行 测试:
制作了一个 non-recursive 方法,该方法将删除双胞胎 - 想法是从非常相似的 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 版本也对我的问题中提到的输入进行排序 How to compare big JSON's?(新版本没有,因此当其中一个对象颠倒顺序时它可能会非常慢 - 在解析期间排序或至少比较两者会更容易双胞胎的邻居作为第一个搜索步骤)
我有两个 Json 对象需要比较如下。我正在使用 Newtonsoft 库进行 Json 解析。
string InstanceExpected = jsonExpected;
string InstanceActual = jsonActual;
var InstanceObjExpected = JObject.Parse(InstanceExpected);
var InstanceObjActual = JObject.Parse(InstanceActual);
而且我正在使用 Fluent Assertions 来比较它。但问题是只有当属性 count/names 不匹配时,Fluent 断言才会失败。如果 json 值不同,则通过。当值不同时我要求失败。
InstanceObjActual.Should().BeEquivalentTo(InstanceObjExpected);
例如,我有实际的和预期的 json 进行比较,如下所示。而用上面的比较方式让他们通过是错误的。
{
"Name": "20181004164456",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}
{
"Name": "AAAAAAAAAAAA",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}
将 json 反序列化为 C# 对象后,正确的方法是在反序列化的 class 中实现 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;
}
}
一种选择是将 json 字符串反序列化为 C# 对象并进行比较。
与使用 JToken.DeepEquals
相比,这种方法需要做更多工作(如@JessedeWit 所建议),但它的优点是可以在测试失败时提供更好的错误消息(请参见下面的屏幕截图)。
您的 json 字符串可以建模为以下内容 class:
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。 运行 测试:
考虑使用 Newtonsoft 提供的 JToken.DeepEquals()
方法。无论您使用哪种测试框架,它看起来都像这样:
Console.WriteLine(JToken.DeepEquals(InstanceObjActual, InstanceObjExpected));
// false
我做了更多的挖掘,并且能够找出为什么 OP 的测试代码没有像预期的那样 运行。我能够通过安装和使用 FluentAssertions.Json nuget 包来修复它。
一件重要的事情:
Be sure to include
using FluentAssertions.Json
otherwise false positives may occur.
测试代码如下:
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);
}
}
运行 测试:
制作了一个 non-recursive 方法,该方法将删除双胞胎 - 想法是从非常相似的 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 版本也对我的问题中提到的输入进行排序 How to compare big JSON's?(新版本没有,因此当其中一个对象颠倒顺序时它可能会非常慢 - 在解析期间排序或至少比较两者会更容易双胞胎的邻居作为第一个搜索步骤)