1

I am trying to compare two JSON objects in C# and get their differences.

considering the following object as source object

{
    "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"
        }
    ]
}

and this object as target object

{
    "name": "Shawn",
    "age": 23,
    "phone": [
        7852698,
        141414
    ],
    "address": [
        {
            "address1": "333",
            "address2": "567"
        },
        {
            "address1": "123",
            "address2": "1414",
            "additionalInfo": {
                "pin": 1236381,
                "landmark": "landmark3"
            }
        }
    ]
}

I would like to compute the difference by comparing each property in the target object with the source object and return the response in the following format.

[
    {
        "fieldName": "name",
        "targetValue": "Shawn",
        "sourceValue": "Kirk"
    },
    {
        "fieldName": "address[0].address1",
        "targetValue": 333,
        "sourceValue": 123
    },
    {
        "fieldName": "address[1].additionalInfo.pin",
        "targetValue": 1236381,
        "sourceValue": 123321
    }
]

fieldName is nothing but the JSON Path to that value. How should I perform this operation in able to get the difference in my JSON objects ?

Code-47
  • 413
  • 5
  • 17

3 Answers3

0

Can you please try below code:

string jsonActual = @"{""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""}]}";
        string jsonExpected = @"{""name"":""Shawn"",""age"":23,""phone"":[7852698,141414],""address"":[{""address1"":""333"",""address2"":""567""},{""address1"":""123"",""address2"":""1414"",""additionalInfo"":{""pin"":1236381,""landmark"":""landmark3""}}]}";

var expectedObject = JsonConvert.DeserializeObject<JObject>(jsonExpected);
var actualObject = JsonConvert.DeserializeObject<JObject>(jsonActual);

List<RootObject> rootObjects = new List<RootObject>();
var obj = CompareObjects(actualObject, expectedObject, rootObjects);

string str = JsonConvert.SerializeObject(obj);

Please add this two method

public class RootObject
        {
            public string fieldName { get; set; }
            public object targetValue { get; set; }
            public object sourceValue { get; set; }
        }

        private static List<RootObject> CompareObjects(JObject source, JObject target, List<RootObject> rootObjects)
        {
            foreach (KeyValuePair<string, JToken> sourcePair in source)
            {
                if (sourcePair.Value.Type == JTokenType.Object)
                {
                    if (target.GetValue(sourcePair.Key) == null)
                    {
                        RootObject rootObject = new RootObject();
                        rootObject.fieldName = sourcePair.Key;
                        rootObject.sourceValue = sourcePair.Value;
                        rootObject.targetValue = "";
                        rootObjects.Add(rootObject);
                    }
                    else if (target.GetValue(sourcePair.Key).Type != JTokenType.Object)
                    {
                        RootObject rootObject = new RootObject();
                        rootObject.fieldName = sourcePair.Key;
                        rootObject.sourceValue = sourcePair.Value;
                        rootObject.targetValue = "";
                        rootObjects.Add(rootObject);
                    }
                    else
                    {
                        rootObjects = CompareObjects(sourcePair.Value.ToObject<JObject>(),
                            target.GetValue(sourcePair.Key).ToObject<JObject>(), rootObjects);
                    }
                }
                else if (sourcePair.Value.Type == JTokenType.Array)
                {
                    if (target.GetValue(sourcePair.Key) == null)
                    {
                        RootObject rootObject = new RootObject();
                        rootObject.fieldName = sourcePair.Key;
                        rootObject.sourceValue = sourcePair.Value;
                        rootObject.targetValue = "";
                        rootObjects.Add(rootObject);
                    }
                    else
                    {
                        rootObjects=CompareArrays(sourcePair.Value.ToObject<JArray>(),
                            target.GetValue(sourcePair.Key).ToObject<JArray>(),rootObjects, sourcePair.Key);
                    }
                }
                else
                {
                    JToken expected = sourcePair.Value;
                    var actual = target.SelectToken(sourcePair.Key);
                    if (actual == null)
                    {
                        RootObject rootObject = new RootObject();
                        rootObject.fieldName = sourcePair.Key;
                        rootObject.sourceValue = sourcePair.Value;
                        rootObject.targetValue = "";
                        rootObjects.Add(rootObject);
                    }
                    else
                    {
                        if (!JToken.DeepEquals(expected, actual))
                        {
                            RootObject rootObject = new RootObject();
                            rootObject.fieldName = sourcePair.Key;
                            rootObject.sourceValue = sourcePair.Value;
                            rootObject.targetValue = target.Property(sourcePair.Key).Value;
                            rootObjects.Add(rootObject);
                        }
                    }
                }
            }
            return rootObjects;
        }

        private static List<RootObject> CompareArrays(JArray source, JArray target, List<RootObject> rootObjects , string arrayName = "")
        {
            for (var index = 0; index < source.Count; index++)
            {

                var expected = source[index];
                if (expected.Type == JTokenType.Object)
                {
                    var actual = (index >= target.Count) ? new JObject() : target[index];
                    rootObjects = CompareObjects(expected.ToObject<JObject>(),
                        actual.ToObject<JObject>(), rootObjects);
                }
                else
                {

                    var actual = (index >= target.Count) ? "" : target[index];
                    if (!JToken.DeepEquals(expected, actual))
                    {
                        if (String.IsNullOrEmpty(arrayName))
                        {
                            RootObject rootObject = new RootObject();
                            rootObject.fieldName = "[" + index + "]";
                            rootObject.sourceValue = expected;
                            rootObject.targetValue = actual;

                            rootObjects.Add(rootObject);
                        }
                        else
                        {

                            RootObject rootObject = new RootObject();
                            rootObject.fieldName = arrayName + "[" + index + "]";
                            rootObject.sourceValue = expected;
                            rootObject.targetValue = actual;

                            rootObjects.Add(rootObject);
                        }
                    }
                }
            }
            return rootObjects;
        }
Dhaval Soni
  • 416
  • 3
  • 13
0

I have compared objects by serializing them.

First here my models are,

To serialize json objects

public class YourClass {
    public string name { get; set; }
    public int age { get; set; }
    public List<long> phone { get; set; }
    public List<Address> address { get; set; }   
}

public class Address {
    public string address1 { get; set; }
    public string address2 { get; set; }
    public string addressLine1 { get; set; }
    public string addressLine2 { get; set; }
    public AdditionalInformation additionalInfo { get; set; }
}

public class AdditionalInformation {
    public int pin { get; set; }
    public string landmark { get; set; }
}

Then to deserialize from this model

public class ResultItem {
    public ResultItem(string fieldName, string targetValue, string sourceValue) {
        this.fieldName = fieldName;
        this.targetValue = targetValue;
        this.sourceValue = sourceValue;
    }
    public string fieldName { get; set; }
    public string targetValue { get; set; }
    public string sourceValue { get; set; }
}

And the following code gets the differences,

        string 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'}]}";

        string json2 = @"{'name': 'Shawn','age': 23,'phone': [7852698,141414],'address': [{'address1': '333','address2': '567'},{'address1': '123','address2': '1414',
        'additionalInfo': {'pin': 1236381,'landmark': 'landmark3'}}]}";

        var sample1 = JsonConvert.DeserializeObject<YourClass>(json1);
        var sample2 = JsonConvert.DeserializeObject<YourClass>(json2);

        var result = new List<ResultItem>();
        if (!sample1.name.Equals(sample2.name))
            result.Add(new ResultItem("name", sample2.name, sample1.name));
        if (sample1.age != sample2.age)
            result.Add(new ResultItem("age", sample2.age.ToString(), sample1.age.ToString()));
        for(int i=0;i< sample1.address.Count; i++) {
            if (sample1.address.Count>i && sample2.address.Count>i && sample1.address[i].address1!=sample2.address[i].address1)
                result.Add(new ResultItem($"address[{i}].address1", sample2.address[i].address1, sample1.address[i].address1));
            if (sample1.address.Count > i && sample2.address.Count > i && sample1.address[i].address2!=sample2.address[i].address2)
                result.Add(new ResultItem($"address[{i}].address2", sample2.address[i].address2, sample1.address[i].address2));
            if (sample1.address.Count > i && sample2.address.Count > i && sample1.address[i].addressLine1!=sample2.address[i].addressLine1)
                result.Add(new ResultItem($"address[{i}].addressLine1", sample2.address[i].addressLine1, sample1.address[i].addressLine1));
            if (sample1.address.Count > i && sample2.address.Count > i && sample1.address[i].addressLine2!=sample2.address[i].addressLine2)
                result.Add(new ResultItem($"address[{i}].addressLine2", sample2.address[i].addressLine2, sample1.address[i].addressLine2));
            if (sample1.address[i].additionalInfo!=null && sample2.address[i].additionalInfo!=null && sample1.address[i].additionalInfo.pin != sample2.address[i].additionalInfo.pin)
                result.Add(new ResultItem($"address[{i}].additionalInfo.pin", sample2.address[i].additionalInfo.pin.ToString(), sample1.address[i].additionalInfo.pin.ToString()));
            if (sample1.address.Count > i && sample2.address.Count > i && sample2.address[i].additionalInfo!=null && sample1.address[i].additionalInfo!=null && !sample1.address[i].additionalInfo.landmark.Equals(sample2.address[i].additionalInfo.landmark))
                result.Add(new ResultItem($"address[{i}].additionalInfo.landmark", sample2.address[i].additionalInfo.landmark, sample1.address[i].additionalInfo.landmark));
        }

        string jsonResult = JsonConvert.SerializeObject(result);

and jsonResult returns the following

[ 
   { 
      "fieldName":"name",
      "targetValue":"Shawn",
      "sourceValue":"Kirk"
   },
   { 
      "fieldName":"address[0].address1",
      "targetValue":"333",
      "sourceValue":"123"
   },
   { 
      "fieldName":"address[0].address2",
      "targetValue":"567",
      "sourceValue":"124"
   },
   { 
      "fieldName":"address[1].address2",
      "targetValue":"1414",
      "sourceValue":"144"
   },
   { 
      "fieldName":"address[1].additionalInfo.pin",
      "targetValue":"1236381",
      "sourceValue":"123321"
   },
   { 
      "fieldName":"address[1].additionalInfo.landmark",
      "targetValue":"landmark3",
      "sourceValue":"landmark1"
   }
]
Furkan Öztürk
  • 1,178
  • 11
  • 24
0

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");
    }
Reza Jenabi
  • 3,884
  • 1
  • 29
  • 34