-1

I'm converting View Model into a JSON Object and inserting into the Database.

Now I want to pick any two JSON objects from the database and compare both the JSON objects and see if there is any change in the attribute value and return the list of differences between them. So I'm looking for a function in C# that compares two JSON-objects, and returns a JSON-object with a list of the differences and if possible more data such as coverage metrics. Can someone help me with it.

Example:

var json1 = @"{ ""name"": ""JohnRob"", ""nickname"":""John"", ""age"": 20, ""married"": false }";

var json2 = @"{ ""name"": ""JohnRob"", ""nickname"":""John"", ""age"": 22, ""married"": true }";

Result: The output should be something like

{ "age":[ 20, 22 ], "married":[ false, true ] }

Venkat457
  • 13
  • 4

2 Answers2

0

The answer will depend on which JSON library you want to use and whether you want to handle the nested properties of complex json objects, or whether it is sufficient to go through the list of properties of the main object.

For example, if you use the Newtonsoft.Json library and only compare first-level properties, this code would be enough:

public static JObject CompareJsonObjects(string json1, string json2)
{
    var obj1 = JObject.Parse(json1);
    var obj2 = JObject.Parse(json2);
    var diff = new JObject();

    foreach (var property in obj1)
    {
        var value1 = property.Value;

        if (obj2.TryGetValue(property.Key, out var value2))
            if (!JToken.DeepEquals(value1, value2))
                diff[property.Key] = new JArray(value1, value2);
        else
            diff[property.Key] = new JArray(value1, null);
    }

    foreach (var property in obj2)
        if (!obj1.ContainsKey(property.Key))
            diff[property.Key] = new JArray(null, property.Value);

    return diff;
}

The common strategy is to go through each property of the first JSON, find the corresponding property of the second JSON by its key, compare them and, if they are different, write them into the result. Do the same if there is no corresponding property of the second object. Similarly, the properties of the second object, which may not exist in the first object, must be handled similarly.

If you use a more modern System.Text.Json and need to recursively check and compare properties of complex objects, the code will be different. We will use similar strategy but recursively.

public static JsonElement CompareJsonObjects(string json1, string json2)
{
    using var doc1 = JsonDocument.Parse(json1);
    using var doc2 = JsonDocument.Parse(json2);

    var obj1 = doc1.RootElement;
    var obj2 = doc2.RootElement;

    return CompareJsonElements(obj1, obj2);
}

private static JsonElement CompareJsonElements(JsonElement elem1, JsonElement elem2)
{
    if (elem1.ValueKind != elem2.ValueKind)
        return CreateDifferenceArray(elem1, elem2);

    switch (elem1.ValueKind)
    {
        case JsonValueKind.Object:
            var result = new Dictionary<string, JsonElement>();
            foreach (var prop in elem1.EnumerateObject())
                if (elem2.TryGetProperty(prop.Name, out var value2))
                {
                    var diff = CompareJsonElements(prop.Value, value2);
                    if (diff.ValueKind != JsonValueKind.Undefined) 
                        result.Add(prop.Name, diff);
                }
                else
                    result.Add(prop.Name, CreateDifferenceArray(prop.Value, default));

            foreach (var prop in elem2.EnumerateObject().Where(prop => !elem1.TryGetProperty(prop.Name, out _)))
                result.Add(prop.Name, CreateDifferenceArray(default, prop.Value));

            return JsonElementFromObject(result);

        case JsonValueKind.Array:
            var areArraysEqual = elem1.GetArrayLength() == elem2.GetArrayLength();
            if (areArraysEqual)
                for (var i = 0; i < elem1.GetArrayLength(); i++)
                    if (!elem1[i].Equals(elem2[i]))
                    {
                        areArraysEqual = false;
                        break;
                    }

            return areArraysEqual ? default : CreateDifferenceArray(elem1, elem2);

        default:
            return elem1.Equals(elem2) ? default : CreateDifferenceArray(elem1, elem2);
    }
}

private static JsonElement CreateDifferenceArray(JsonElement elem1, JsonElement elem2)
{
    var array = new JsonElement[] { elem1, elem2 };
    return JsonElementFromObject(array);
}

private static JsonElement JsonElementFromObject(object obj)
{
    var jsonString = JsonSerializer.Serialize(obj);
    using var doc = JsonDocument.Parse(jsonString);
    return doc.RootElement.Clone();
}
Vadim Martynov
  • 8,602
  • 5
  • 31
  • 43
0

Previous thread here talked about a diff program for this called jsondiffpatch. Looks like it does what you're looking for. It's main object let's you pass a left and right json to diff.

Format of call is just

jdp.Patch(left, patch);

tanr
  • 99
  • 6