private static void AddExpectedToken(ObjectDiffPatchResult item, JToken expectedToken, string fieldName) { if (item.ExpectedValues == null) { item.ExpectedValues = new JObject(); } item.ExpectedValues[fieldName] = expectedToken; }
private static void AddActualToken(ObjectDiffPatchResult item, JToken actualToken, string fieldName) { if (item.ActualValues == null) { item.ActualValues = new JObject(); } item.ActualValues[fieldName] = actualToken; }
public void Test_DiffField_handling_nulls() { var token = JToken.Parse("{\"field\":\"value\"}"); var result = new ObjectDiffPatchResult(); ObjectDiffPatch.DiffField("field", null, token, result); result.NewValues["field"].Should().Be(token); result.NewValues["field"].HasValues.Should().BeTrue(); result.OldValues["field"].HasValues.Should().BeFalse(); result = new ObjectDiffPatchResult(); ObjectDiffPatch.DiffField("field", token, null, result); result.OldValues["field"].Should().Be(token); result.OldValues["field"].HasValues.Should().BeTrue(); result.NewValues["field"].HasValues.Should().BeFalse(); }
/// <summary> /// Compares two objects and generates the differences between them. /// </summary> /// <typeparam name="T">The type of the T.</typeparam> /// <param name="expected">The expected object.</param> /// <param name="actual">The actual object.</param> /// <returns></returns> public static ObjectDiffPatchResult GenerateDiff <T>(T expected, T actual) where T : class { if (typeof(JObject).IsAssignableFrom(typeof(T))) { var expectedJson = expected as JObject; var actualJson = actual as JObject; return(Diff(expectedJson, actualJson)); } if (typeof(JArray).IsAssignableFrom(typeof(T))) { var expectedJson = expected as JArray; var actualJson = actual as JArray; var diff = new ObjectDiffPatchResult(); if (expectedJson == null && actualJson == null) { return(diff); } if (expectedJson == null || actualJson == null) { diff.ExpectedValues = new JObject { { PrefixNullObject, expectedJson == null ? "null" : "not null" } }; diff.ActualValues = new JObject { { PrefixNullObject, actualJson == null ? "null" : "not null" } }; return(diff); } if (expectedJson.Count != actualJson.Count) { diff.ExpectedValues = new JObject { { PrefixArraySize, expectedJson.Count } }; diff.ActualValues = new JObject { { PrefixArraySize, actualJson.Count } }; return(diff); } for (var index = 0; index < expectedJson.Count; index++) { var expectedJObject = expectedJson[index] as JObject; var actualJObject = actualJson[index] as JObject; var result = Diff(expectedJObject, actualJObject); if (result.ExpectedValues != null) { foreach (var pair in result.ExpectedValues) { if (diff.ExpectedValues == null) { diff.ExpectedValues = new JObject(); } diff.ExpectedValues.Add($"{pair.Key}[{index}]", pair.Value); } } if (result.ActualValues != null) { foreach (var pair in result.ActualValues) { if (diff.ActualValues == null) { diff.ActualValues = new JObject(); } diff.ActualValues.Add($"{pair.Key}[{index}]", pair.Value); } } } return(diff); } else { // not a JObject or a JArray // ensure the serializer will not ignore null values var writer = GetJsonSerializer(); var expectedJson = expected != null?JObject.FromObject(expected, writer) : null; var actualJson = actual != null?JObject.FromObject(actual, writer) : null; return(Diff(expectedJson, actualJson)); } }
private static void AddToken(ObjectDiffPatchResult item, string fieldName, ObjectDiffPatchResult diff) { AddToken(item, fieldName, diff.ExpectedValues, diff.ActualValues); }
private static void AddToken(ObjectDiffPatchResult item, string fieldName, JToken expectedToken, JToken actualToken) { AddExpectedToken(item, expectedToken, fieldName); AddActualToken(item, actualToken, fieldName); }
private static void DiffField(string fieldName, JToken source, JToken target, ObjectDiffPatchResult result = null) { if (result == null) { result = new ObjectDiffPatchResult(); } if (source == null) { if (target != null) { AddToken(result, fieldName, null, target); } } else if (target == null) { AddToken(result, fieldName, source, null); } else if (source.Type == JTokenType.Object) { var v = target as JObject; var r = Diff(source as JObject, v); if (!r.AreEqual) { AddToken(result, fieldName, r); } } else { var aT = target as JArray; var aS = source as JArray; // ReSharper disable once SwitchStatementMissingSomeCases switch (source.Type) { case JTokenType.Array when aS == null || aT == null: case JTokenType.Array when(aS.Count == 0 || aT.Count == 0) && aS.Count != aT.Count: AddToken(result, fieldName, source, target); break; case JTokenType.Array: { var arrayDiff = new ObjectDiffPatchResult(); var minCount = Math.Min(aS.Count, aT.Count); for (var i = 0; i < Math.Max(aS.Count, aT.Count); i++) { if (i < minCount) { DiffField(i.ToString(), aS[i], aT[i], arrayDiff); } else if (i >= aS.Count) { AddActualToken(arrayDiff, aT[i], i.ToString()); } else { AddExpectedToken(arrayDiff, aS[i], i.ToString()); } } if (arrayDiff.AreEqual) { return; } if (aS.Count != aT.Count) { AddToken(arrayDiff, PrefixArraySize, aS.Count, aT.Count); } AddToken(result, fieldName, arrayDiff); break; } case JTokenType.Integer when target.Type != JTokenType.Integer: { var sourceValue = (JValue)source; if (target is JValue targetValue) { try { var objA = Convert.ChangeType(sourceValue.Value, targetValue.Value.GetType()); if (!objA.Equals(targetValue.Value)) { AddToken(result, fieldName, source, target); } } catch { AddToken(result, fieldName, source, target); } } else if (!JToken.DeepEquals(source, target)) { AddToken(result, fieldName, source, target); } break; } default: { if (!JToken.DeepEquals(source, target)) { AddToken(result, fieldName, source, target); } break; } } } }
private static ObjectDiffPatchResult Diff(JObject expected, JObject actual) { var result = new ObjectDiffPatchResult(); // check for null values if (expected == null && actual == null) { return(result); } if (expected == null || actual == null) { result.ExpectedValues = expected; result.ActualValues = actual; return(result); } // compare internal fields var removedNew = new JArray(); var removedOld = new JArray(); JToken token; // start by iterating in source fields foreach (var pair in expected) { // ignore null values (i.e. null values = missing value) if (pair.Value.Type == JTokenType.Null) { continue; } // check if field exists if (!actual.TryGetValue(pair.Key, out token)) { AddExpectedToken(result, pair.Value, pair.Key); removedNew.Add(pair.Key); } // compare field values else { DiffField(pair.Key, pair.Value, token, result); } } // then iterate in target fields that are not present in source foreach (var pair in actual) { // ignore null values (i.e. null values = missing value) if (pair.Value.Type == JTokenType.Null) { continue; } // ignore already compared values if (expected.TryGetValue(pair.Key, out token)) { continue; } // add missing tokens removedOld.Add(pair.Key); AddActualToken(result, pair.Value, pair.Key); } if (removedOld.Count > 0) { AddExpectedToken(result, removedOld, PrefixRemovedFields); } if (removedNew.Count > 0) { AddActualToken(result, removedNew, PrefixRemovedFields); } return(result); }