Find and Return JSON Differences Using Newtonsoft in C#

Get differences in two JSON objects using newtonsoft

If you want to compare by newtonsoft you can use bellow code

public static class ExtensionJson
{
public static IEnumerable<string> CompareJson(this JObject json, JObject json2, string path)
{
var result = new List<string>();
foreach (var property in json.Properties())
{
json2.TryGetValue(property.Name, out var itemTokenTwo);
if (itemTokenTwo == null)
{
result.Add($"{{ \"fieldName\": {path}{property.Path},\"targetValue\": {property.Value},\"sourceValue\": NULL }}");
continue;
}

if (property.Value.Type == JTokenType.Array && itemTokenTwo.Type == JTokenType.Array)
{
result.AddRange(JArray.Parse(property.Value.ToString()).CompareJson(JArray.Parse(itemTokenTwo.ToString()),
(!string.IsNullOrWhiteSpace(path) ? path + "." : default) + property.Path));
continue;
}

if (property.Value.Type == JTokenType.Object && itemTokenTwo.Type == JTokenType.Object)
{
result.AddRange(JObject.Parse(property.Value.ToString()).CompareJson(JObject.Parse(itemTokenTwo.ToString()), (!string.IsNullOrWhiteSpace(path) ? path + "." : default) + property.Path));
continue;
}

if (property.Value.Type != itemTokenTwo.Type)
{
result.Add($"{{ \"fieldName\": {path}{property.Path},\"targetValue\": {property.Value},\"sourceValue\": {itemTokenTwo} }}");
continue;
}

if (property.Value.ToString() != itemTokenTwo.Value<string>().ToString())
{
result.Add($"{{ \"fieldName\": {path}.{property.Path},\"targetValue\": {property.Value},\"sourceValue\": {itemTokenTwo} }}");
}
}

return result;
}

public static IEnumerable<string> CompareJson(this JArray json, JArray json2, string path)
{
var result = new List<string>();

foreach (var jToken in json)
{
var jToken2 = json2.SelectToken(jToken.Path);

if (jToken2 == null)
{
result.Add($"{{ \"fieldName\": {path}{jToken.Path},\"targetValue\": {jToken},\"sourceValue\": NULL }}");
continue;
}

if (jToken is JObject && jToken2 is JObject)
{
result.AddRange(JObject.Parse(jToken.ToString())
.CompareJson(JObject.Parse(jToken2.ToString()), path + jToken.Path));
continue;
}
if (jToken is JArray && jToken2 is JArray)
{
result.AddRange(JArray.Parse(jToken.ToString()).CompareJson(JArray.Parse(jToken2.ToString()), path + jToken.Path));
continue;
}

if (jToken is JProperty jProperty && jToken2 is JProperty token2)
{
if (jProperty.Value != token2.Value)
{
result.Add($"{{ \"fieldName\": {path}{jProperty.Path},\"targetValue\": {jProperty.Value},\"sourceValue\": {jToken2} }}");

}
}
else if (jToken is JValue value1 && jToken2 is JValue value2)
{
if (!value1.Equals(value2))
{
result.Add(
$"{{ \"fieldName\": {path}{jToken.Path},\"targetValue\": {value1},\"sourceValue\": {value2} }}");
}
}
else if (jToken is JProperty property)
{
result.Add($"{{ \"fieldName\": {path}{property.Path},\"targetValue\": {property.Value},\"sourceValue\": {jToken2} }}");

}
else
{
result.Add($"{{ \"fieldName\": {path}{((JProperty)jToken2).Path},\"targetValue\": {((JProperty)jToken2).Value},\"sourceValue\": {jToken} }}");
}

}

return result;
}
}

Main:

 var json1 = @"{'name': 'Kirk',
'age': 23,
'phone': [
123123123,
141414
],
'address': [
{
'address1': '123',
'address2': '124'
},
{
'address1': '123',
'address2': '144',
'additionalInfo': {
'pin': 123321,
'landmark': 'landmark1'
}
},
{
'address1': '1223',
'addressLine2': '1242'
}
]
}";
var json2 = @"{
'name': 'Shawn',
'age': 23,
'phone': [
7852698,
141414
],
'address': [
{
'address1': '333',
'address2': '567'
},
{
'address1': '123',
'address2': '1414',
'additionalInfo': {
'pin': 1236381,
'landmark': 'landmark3'
}
}
]
}";

JObject json1O = JObject.Parse(json1);
JObject json2O = JObject.Parse(json2);
var items = JObject.Parse(json1).CompareJson(JObject.Parse(json2), json1O.Path);

foreach (var property in items)
{
Console.WriteLine(property + "\n\n");
}

Why Controller.Json and Newtonsoft Json return different results?

Because Controller.Json produces JsonResult object, which contains additional parameters. The overview of those can be found here. Whereas Newtonsoft serializes the specified object to a JSON string.

Data wise (that you serialize), they do produce the same result.


If you want to append to those properties, it's the normal use of getters and setters on an object:

return new JsonResult(result)
{
StatusCode = 200
};

And this object should be returned from the controller instead of return StatusCode(200, Json(data));


It's also worth pointing out that Json is an extension method (helper method) for the JsonResult. They both return the same thing, an instance of JsonResult (which in turn inherits ActionResult).

Calling Json or JsonResult is a matter of preference, you'll have to write less for Json and won't have to declare instances of JsonResult in your controller as it'll be done behind the scenes for you.

How to compare two Json objects using C#

I did a bit more digging and was able to find out why the OP's test code doesn't run as expected. I was able to fix it by installing and using the FluentAssertions.Json nuget package.

One important thing:

Be sure to include using FluentAssertions.Json otherwise false
positives may occur.

Test code is the following:

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

Running the test:

Unit test results

Detect differences between two json files in c#

I recommend you use Weakly-Typed JSON Serialization and write a routine that uses JsonObject like this:

String JsonDifferenceReport(String objectName,
JsonObject first,
JsonObject second)
{
if(String.IsNullOrEmpty(objectName))
throw new ArgumentNullException("objectName");
if(null==first)
throw new ArgumentNullException("first");
if(null==second)
throw new ArgumentNullException("second");
List<String> allKeys = new List<String>();
foreach(String key in first.Keys)
if (!allKeys.Any(X => X.Equals(key))) allKeys.Add(key);
foreach(String key in second.Keys)
if (!allKeys.Any(X => X.Equals(key))) allKeys.Add(key);
String results = String.Empty;
foreach(String key in allKeys)
{
JsonValue v1 = first[key];
JsonValue v1 = second[key];
if (((null==v1) != (null==v2)) || !v1.Equals(v2))
{
if(String.IsNullOrEmpty(results))
{
results = "differences: {\n";
}
results += "\t" + objectName + ": {\n";
results += "\t\tfield: " + key + ",\n";
results += "\t\toldvalue: " + (null==v1)? "null" : v1.ToString() + ",\n";
results += "\t\tnewvalue: " + (null==v2)? "null" : v2.ToString() + "\n";
results += "\t}\n";
}
}
if(!String.IsNullOrEmpty(results))
{
results += "}\n";
}
return results;
}

Your choice whether to get reports recursively inside JsonValue v1 and v2, instead of just using their string representation as I did here.

If you wanted to go recursive, it might change the above like this:

  if ((null==v1) || (v1.JsonType == JsonType.JsonPrimitive)
|| (null==v2) || (v2.JsonType == JsonType.JsonPrimitive))
{
results += "\t\tfield: " + key + ",\n";
results += "\t\toldvalue: " + (null==v1) ? "null" : v1.ToString() + ",\n";
results += "\t\tnewvalue: " + (null==v2) ? "null" : v2.ToString() + "\n";
}
else
{
results + JsonDifferenceReport(key, v1, v2);
}

-Jesse



Related Topics



Leave a reply



Submit