Find and return JSON differences using newtonsoft in C#?

C#Jsonjson.net

C# Problem Overview


I'd like to get a list of the JSON parts that don't match when doing a comparison using Newtonsoft.

I have this code that compares:

JObject xpctJSON = JObject.Parse(expectedJSON);
JObject actJSON = JObject.Parse(actualJSON);

bool res = JToken.DeepEquals(xpctJSON, actJSON);

But can't find anything that returns the diff's.

C# Solutions


Solution 1 - C#

Just to help future queries. There's a nice json diff tool I came across. It works flawlessly for diff/patch of json structures:

jsondiffpatch.net There's also a nuget package for it.

usage is straightforward.

var jdp = new JsonDiffPatch();
JToken diffResult = jdp.Diff(leftJson, rightJson);

Solution 2 - C#

Here is a recursive version that I wrote. You call CompareObjects with two JObjects and it returns a list of the differences. You call CompareArrays with two JArrays and it compares the arrays. Arrays and Objects can be nested inside each other.

UPDATE: @nttakr points out in the comment below that this method is actually a partial difference algorithm. It only tells you about differences from the point of view of the source list. If a key doesn't exist in the source but does exist in the target list, that difference will be ignored. This is by design for my testing requirements. This allows you to test for just then items you want without having to delete them from the target before the comparisons are done.

    /// <summary>
    /// Deep compare two NewtonSoft JObjects. If they don't match, returns text diffs
    /// </summary>
    /// <param name="source">The expected results</param>
    /// <param name="target">The actual results</param>
    /// <returns>Text string</returns>

    private static StringBuilder CompareObjects(JObject source, JObject target)
    {
        StringBuilder returnString = new StringBuilder();
        foreach (KeyValuePair<string, JToken> sourcePair in source)
        {
            if (sourcePair.Value.Type == JTokenType.Object)
            {
                if (target.GetValue(sourcePair.Key) == null)
                {
                    returnString.Append("Key " + sourcePair.Key
                                        + " not found" + Environment.NewLine);
                }
                else if (target.GetValue(sourcePair.Key).Type != JTokenType.Object) {
                    returnString.Append("Key " + sourcePair.Key
                                        + " is not an object in target" + Environment.NewLine);
                }                    
                else
                {
                    returnString.Append(CompareObjects(sourcePair.Value.ToObject<JObject>(),
                        target.GetValue(sourcePair.Key).ToObject<JObject>()));
                }
            }
            else if (sourcePair.Value.Type == JTokenType.Array)
            {
                if (target.GetValue(sourcePair.Key) == null)
                {
                    returnString.Append("Key " + sourcePair.Key
                                        + " not found" + Environment.NewLine);
                }
                else
                {
                    returnString.Append(CompareArrays(sourcePair.Value.ToObject<JArray>(),
                        target.GetValue(sourcePair.Key).ToObject<JArray>(), sourcePair.Key));
                }
            }
            else
            {
                JToken expected = sourcePair.Value;
                var actual = target.SelectToken(sourcePair.Key);
                if (actual == null)
                {
                    returnString.Append("Key " + sourcePair.Key
                                        + " not found" + Environment.NewLine);
                }
                else
                {
                    if (!JToken.DeepEquals(expected, actual))
                    {
                        returnString.Append("Key " + sourcePair.Key + ": "
                                            + sourcePair.Value + " !=  "
                                            + target.Property(sourcePair.Key).Value
                                            + Environment.NewLine);
                    }
                }
            }
        }
        return returnString;
    }

    /// <summary>
    /// Deep compare two NewtonSoft JArrays. If they don't match, returns text diffs
    /// </summary>
    /// <param name="source">The expected results</param>
    /// <param name="target">The actual results</param>
    /// <param name="arrayName">The name of the array to use in the text diff</param>
    /// <returns>Text string</returns>

    private static StringBuilder CompareArrays(JArray source, JArray target, string arrayName = "")
    {
        var returnString = new StringBuilder();
        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];
                returnString.Append(CompareObjects(expected.ToObject<JObject>(),
                    actual.ToObject<JObject>()));
            }
            else
            {

                var actual = (index >= target.Count) ? "" : target[index];
                if (!JToken.DeepEquals(expected, actual))
                {
                    if (String.IsNullOrEmpty(arrayName))
                    {
                        returnString.Append("Index " + index + ": " + expected
                                            + " != " + actual + Environment.NewLine);
                    }
                    else
                    {
                        returnString.Append("Key " + arrayName
                                            + "[" + index + "]: " + expected
                                            + " != " + actual + Environment.NewLine);
                    }
                }
            }
        }
        return returnString;
    }

Solution 3 - C#

There is my solution based on the ideas from the previous answers:

public static JObject FindDiff(this JToken Current, JToken Model)
{
	var diff = new JObject();
	if (JToken.DeepEquals(Current, Model)) return diff;

	switch(Current.Type)
	{
		case JTokenType.Object:
			{
				var current = Current as JObject;
				var model = Model as JObject;
				var addedKeys = current.Properties().Select(c => c.Name).Except(model.Properties().Select(c => c.Name));
				var removedKeys = model.Properties().Select(c => c.Name).Except(current.Properties().Select(c => c.Name));
				var unchangedKeys = current.Properties().Where(c => JToken.DeepEquals(c.Value, Model[c.Name])).Select(c => c.Name);
				foreach (var k in addedKeys)
				{
					diff[k] = new JObject
					{
						["+"] = Current[k]
					};
				}
				foreach (var k in removedKeys)
				{
					diff[k] = new JObject
					{
						["-"] = Model[k]
					};
				}
				var potentiallyModifiedKeys = current.Properties().Select(c => c.Name).Except(addedKeys).Except(unchangedKeys);
				foreach (var k in potentiallyModifiedKeys)
				{
					var foundDiff = FindDiff(current[k], model[k]);
					if(foundDiff.HasValues) diff[k] = foundDiff;
				}
			}
			break;
		case JTokenType.Array:
			{
				var current = Current as JArray;
				var model = Model as JArray;
				var plus = new JArray(current.Except(model, new JTokenEqualityComparer()));
				var minus = new JArray(model.Except(current, new JTokenEqualityComparer()));
                if (plus.HasValues) diff["+"] = plus;
                if (minus.HasValues) diff["-"] = minus;
			}
			break;
		default:
			diff["+"] = Current;
			diff["-"] = Model;
			break;
	}

	return diff;
}

Solution 4 - C#

This is relatively old question but posting one of the possible ways to solve this, assuming that the result you want is exactly which property values are changed

   string sourceJsonString = "{'name':'John Doe','age':'25','hitcount':34}";
   string targetJsonString = "{'name':'John Doe','age':'26','hitcount':30}";

   JObject sourceJObject = JsonConvert.DeserializeObject<JObject>(sourceJsonString);
   JObject targetJObject = JsonConvert.DeserializeObject<JObject>(targetJsonString);

   if (!JToken.DeepEquals(sourceJObject, targetJObject))
   {
     foreach (KeyValuePair<string, JToken> sourceProperty in sourceJObject)
     {
         JProperty targetProp = targetJObject.Property(sourceProperty.Key);
          
          if (!JToken.DeepEquals(sourceProperty.Value, targetProp.Value))
          {
              Console.WriteLine(string.Format("{0} property value is changed", sourceProperty.Key));
          }
          else
          {
              Console.WriteLine(string.Format("{0} property value didn't change", sourceProperty.Key));
          }
      }
   }
   else
   {
      Console.WriteLine("Objects are same");
   }  

Note: This has not been tested for very deep hierarchy.

Solution 5 - C#

NOTE the following libraries:

using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

I'm not entirely sure I understand your question correctly. I am assuming you are trying to identify what keys are missing from the actual JSON.

If you are just interested in the missing KEYS the below code will help you, if not, please provide an example of the types of differences you are trying to identify.

  public IEnumerable<JProperty> DoCompare(string expectedJSON, string actualJSON)
    {
        // convert JSON to object
        JObject xptJson = JObject.Parse(expectedJSON);
        JObject actualJson = JObject.Parse(actualJSON);

        // read properties
        var xptProps = xptJson.Properties().ToList();
        var actProps = actualJson.Properties().ToList();

        // find missing properties
        var missingProps = xptProps.Where(expected => actProps.Where(actual => actual.Name == expected.Name).Count() == 0);

        return missingProps;
    }

NOTE that if the this method returns an empty IEnumerable then the ACTUAL JSON has all the keys required according to the structure of the expected JSON.

NOTE: the actual JSON could still have more keys that the expected JSON does not required.

to explain my notes further...

assume your expected JSON is:

{ Id: 1, Name: "Item One", Value: "Sample" }

and that your ACTUAL JSON is :

{ Id: 1, Name: "Item One", SomeProp: "x" }

the above function will tell you that the Value key is missing, but will not mention anything about the SomeProp key...unless your swap the input parameters around.

Solution 6 - C#

This is pretty much and old thread, but since I came here a few month back to look for a reliable tool and couldn't find one, I have written my own, you may use it if you are looking something similar to the following:

JSON 1

{
  "name":"John",
  "age":30,
  "cars": {
    "car1":"Ford",
    "car2":"BMW",
    "car3":"Fiat"
  }
 }

JSON 2

{
  "name":"John",
  "cars": {
    "car1":"Ford",
    "car2":"BMW",
    "car3":"Audi",
    "car4":"Jaguar"
  }
 }

Usage


 var j1 = JToken.Parse(Read(json1));
 var j2 = JToken.Parse(Read(json2));
 
 var diff = JsonDifferentiator.Differentiate(j1,j2);
 

Result

{
  "-age": 30,
  "*cars": {
    "*car3": "Fiat",
    "+car4": "Jaguar"
  }
}

Feel free to checkout the source code and look at the tests, your feedbacks are welcome :)

https://www.nuget.org/packages/JsonDiffer

Solution 7 - C#

None of the answers here were quite what I needed.

Here is a method that returns a JObject for each of the two objects compared. The JObjects contain only properties that differ. This is good for scanning changes to an entity and storing a before and after snapshot (serialize the JObjects).

NB: The change scanning occurs only on top level properties.

        private Tuple<JObject, JObject> GetDeltaState<TRead>(TRead before, TRead after)
    {
        if (before == null && after == null)
            return new Tuple<JObject, JObject>(null, null);

        JObject beforeResult;
        JObject afterResult;

        // If one record is null then we don't need to scan for changes
        if (before == null ^ after == null)
        {
            beforeResult = before == null ? null : JObject.FromObject(before, _jsonSerializer);
            afterResult = after == null ? null : JObject.FromObject(after, _jsonSerializer);

            return new Tuple<JObject, JObject>(beforeResult, afterResult);
        }

        beforeResult = new JObject();
        afterResult = new JObject();

        JObject beforeState = JObject.FromObject(before, _jsonSerializer);
        JObject afterState = JObject.FromObject(after, _jsonSerializer);

        // Get unique properties from each object
        IEnumerable<JProperty> properties = beforeState.Properties().Concat(afterState.Properties()).DistinctBy(x => x.Name);

        foreach (JProperty prop in properties)
        {
            JToken beforeValue = beforeState[prop.Name];
            JToken afterValue = afterState[prop.Name];

            if (JToken.DeepEquals(beforeValue, afterValue))
                continue;

            beforeResult.Add(prop.Name, beforeValue);
            afterResult.Add(prop.Name, afterValue);
        }

        return new Tuple<JObject, JObject>(beforeResult, afterResult);
    }

Solution 8 - C#

I have converted to be more accurate in array of objects

public static JObject FindDiff(this JToken leftJson, JToken rightJson)
{
    var difference = new JObject();
    if (JToken.DeepEquals(leftJson, rightJson)) return difference;

    switch (leftJson.Type) {
        case JTokenType.Object:
            {
                var LeftJSON = leftJson as JObject;
                var RightJSON = rightJson as JObject;
                var RemovedTags = LeftJSON.Properties().Select(c => c.Name).Except(RightJSON.Properties().Select(c => c.Name));
                var AddedTags = RightJSON.Properties().Select(c => c.Name).Except(LeftJSON.Properties().Select(c => c.Name));
                var UnchangedTags = LeftJSON.Properties().Where(c => JToken.DeepEquals(c.Value, RightJSON[c.Name])).Select(c => c.Name);
                foreach(var tag in RemovedTags)
                {
                    difference[tag] = new JObject
                    {
                        ["-"] = LeftJSON[tag]
                    };
                }
                foreach(var tag in AddedTags)
                {
                    difference[tag] = new JObject
                    {
                        ["-"] = RightJSON[tag]
                    };
                }
                var ModifiedTags = LeftJSON.Properties().Select(c => c.Name).Except(AddedTags).Except(UnchangedTags);
                foreach(var tag in ModifiedTags)
                {
                    var foundDifference = Compare(LeftJSON[tag], RightJSON[tag]);
                    if (foundDifference.HasValues) {
                        difference[tag] = foundDifference;
                    }
                }
            }
            break;
        case JTokenType.Array:
            {
                var LeftArray = leftJson as JArray;
                var RightArray = rightJson as JArray;

                if (LeftArray != null && RightArray != null) {
                    if (LeftArray.Count() == RightArray.Count()) {
                        for (int index = 0; index < LeftArray.Count(); index++)
                        {
                            var foundDifference = Compare(LeftArray[index], RightArray[index]);
                            if (foundDifference.HasValues) {
                                difference[$"{index}"] = foundDifference;
                            }
                        }
                    }
                    else {
                        var left = new JArray(LeftArray.Except(RightArray, new JTokenEqualityComparer()));
                        var right = new JArray(RightArray.Except(LeftArray, new JTokenEqualityComparer()));
                        if (left.HasValues) {
                            difference["-"] = left;
                        }
                        if (right.HasValues) {
                            difference["+"] = right;
                        }
                    }
                }
            }
            break;
        default:
            difference["-"] = leftJson;
            difference["+"] = rightJson;
            break;
    }

    return difference;
}

Solution 9 - C#

    public static void Validate(JToken actual, JToken expected, IList<string> diffMessages)
    {
        if (actual == null && expected == null)
        {
            // handle accroding to requirement
            return;
        }

        if (actual == null)
        {
            diffMessages.Add($"Diff on {expected.Path}: actual - null, expected - {expected}");
            return;
        }

        if (expected == null)
        {
            diffMessages.Add($"Diff on {actual.Path}: actual - {actual}, expected - null");
            return;
        }

        if (actual.Type != JTokenType.Object && actual.Type != JTokenType.Array && actual.Type != JTokenType.Property)
        {
            if (!JToken.DeepEquals(actual, expected))
            {
                diffMessages.Add($"Diff on {actual.Path}: actual- {actual}, expected - {expected}");
            }

            return;
        }


        // recursion

        foreach (var jItem in actual)
        {
            var newExpected = expected.Root.SelectToken(jItem.Path);
            Validate(jItem, newExpected, diffMessages);
        }
        
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser167908View Question on Stackoverflow
Solution 1 - C#FaheemView Answer on Stackoverflow
Solution 2 - C#WalterView Answer on Stackoverflow
Solution 3 - C#Dzmitry PaliakouView Answer on Stackoverflow
Solution 4 - C#PravinView Answer on Stackoverflow
Solution 5 - C#WaseemSView Answer on Stackoverflow
Solution 6 - C#Amin MView Answer on Stackoverflow
Solution 7 - C#Ste BrownView Answer on Stackoverflow
Solution 8 - C#Rohith DaruriView Answer on Stackoverflow
Solution 9 - C#MinghaoPanView Answer on Stackoverflow