Last active
March 14, 2018 04:36
-
-
Save cameronism/6e48d8cf7e98a2d2bd6bc668309757ed to your computer and use it in GitHub Desktop.
JObject based JSON diff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var pairs = new (object, object)[] | |
{ | |
(new {}, new {}), | |
(new { a = 1 }, new { a = "1" }), | |
(new { a = "1", b = 2 }, new { a = 1 }), | |
(new { a = "1", c = 2 }, new { a = 1, c = 2 }), | |
(new { a = "1", c = new {} }, new { a = 1, c = new {} }), | |
(new { a = new { a = 1 } }, new { a = new {} }), | |
(new { a = new[] { 0 } }, new { a = new int[] {} }), | |
(new { b = true }, new { b = false }), | |
(new { b = (object)null }, new { b = false }), | |
}; | |
foreach (var (before, after) in pairs) | |
{ | |
var a = JsonConvert.SerializeObject(before); | |
var b = JsonConvert.SerializeObject(after); | |
new | |
{ | |
before = a, | |
after = b, | |
diff1 = JsonDiff.Diff(a, b).Select(d => new { d.Property, d.OldType, d.Newype, OldValue = d.OldValue?.ToString(), NewValue = d.NewValue?.ToString() }), | |
diff2 = JsonDiff.Diff(b, a).Select(d => new { d.Property, d.OldType, d.Newype, OldValue = d.OldValue?.ToString(), NewValue = d.NewValue?.ToString() }), | |
}.Dump(); | |
} | |
} | |
internal class JsonDiff | |
{ | |
public string Property { get; set; } | |
public JTokenType OldType { get; set; } | |
public JTokenType Newype { get; set; } | |
public JToken OldValue { get; set; } | |
public JToken NewValue { get; set; } | |
public static List<JsonDiff> Diff(string oldValue, string newValue) | |
{ | |
var results = new List<JsonDiff>(); | |
Same(JObject.Parse(oldValue), JObject.Parse(newValue), results); | |
return results; | |
} | |
private static bool Same(JToken oldt, JToken newt) | |
{ | |
if (oldt.Type != newt.Type) return false; | |
switch (oldt.Type) | |
{ | |
case JTokenType.Object: return Same((JObject)oldt, (JObject)newt, results: null); | |
case JTokenType.Array: | |
var olda = (JArray)oldt; | |
var newa = (JArray)newt; | |
return olda.Count == newa.Count && | |
Enumerable.Range(0, olda.Count) | |
.All(i => Same(olda[i], newa[i])); | |
} | |
return oldt.Equals(newt); | |
} | |
private static bool Same(JObject oldj, JObject newj, List<JsonDiff> results) | |
{ | |
foreach (var prop in oldj.Properties()) | |
{ | |
var name = prop.Name; | |
var oldv = prop.Value; | |
var newv = newj[name]; | |
if (newv == null || !Same(oldv, newv)) | |
{ | |
if (results == null) return false; | |
results.Add(new JsonDiff | |
{ | |
Property = name, | |
OldValue = oldv, | |
OldType = oldv.Type, | |
NewValue = newv, | |
Newype = newv == null ? JTokenType.Undefined : newv.Type, | |
}); | |
} | |
} | |
foreach (var prop in newj.Properties()) | |
{ | |
var name = prop.Name; | |
var newv = prop.Value; | |
// just grab the missing things now | |
if (oldj[name] == null) | |
{ | |
if (results == null) return false; | |
results.Add(new JsonDiff | |
{ | |
Property = name, | |
NewValue = newv, | |
Newype = newv.Type, | |
OldType = JTokenType.Undefined, | |
}); | |
} | |
} | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment