在C#中使用newtonsoft查找并返回JSON差异?

我想得到一个使用Newtonsoft进行比较时不匹配的JSON部分列表。

我有这个代码比较:

JObject xpctJSON = JObject.Parse(expectedJSON); JObject actJSON = JObject.Parse(actualJSON); bool res = JToken.DeepEquals(xpctJSON, actJSON); 

但找不到任何可以返回差异的东西。

这是一个相对古老的问题,但是发布了解决此问题的可能方法之一,假设您想要的结果正是更改了哪些属性值

  string sourceJsonString = "{'name':'John Doe','age':'25','hitcount':34}"; string targetJsonString = "{'name':'John Doe','age':'26','hitcount':30}"; JObject sourceJObject = JsonConvert.DeserializeObject(sourceJsonString); JObject targetJObject = JsonConvert.DeserializeObject(targetJsonString); if (!JToken.DeepEquals(sourceJObject, targetJObject)) { foreach (KeyValuePair 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"); } 

注意:尚未针对非常深层次的层次结构进行测试。

这是我写的递归版本。 您使用两个JObject调用CompareObjects,它返回差异列表。 您使用两个JArrays调用CompareArrays并比较数组。 数组和对象可以互相嵌套。

更新 :@nttakr在下面的评论中指出,这种方法实际上是一种偏差算法。 它只会告诉您与源列表的不同之处。 如果源中不存在键但目标列表中存在该键,则将忽略该差异。 这是我的测试要求的设计。 这使您可以测试所需的项目,而无需在完成比较之前将其从目标中删除。

  ///  /// Deep compare two NewtonSoft JObjects. If they don't match, returns text diffs ///  /// The expected results /// The actual results /// Text string private static StringBuilder CompareObjects(JObject source, JObject target) { StringBuilder returnString = new StringBuilder(); foreach (KeyValuePair 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(), target.GetValue(sourcePair.Key).ToObject())); } } 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(), target.GetValue(sourcePair.Key).ToObject(), 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; } ///  /// Deep compare two NewtonSoft JArrays. If they don't match, returns text diffs ///  /// The expected results /// The actual results /// The name of the array to use in the text diff /// Text string 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(), actual.ToObject())); } 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; } 

只是为了帮助未来的查询。 我遇到了一个很好的json diff工具。 它对于json结构的diff / patch完美无缺:

jsondiffpatch.net还有一个nuget包。

用法很简单。

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

请注意以下库:

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

我不完全确定我正确理解你的问题。 我假设您正在尝试确定实际JSON中缺少哪些键。

如果您只对缺少的KEYS感兴趣,以下代码将对您有所帮助,如果没有,请提供您尝试识别的差异类型的示例。

  public IEnumerable 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; } 

注意,如果this方法返回一个空的IEnumerable,那么ACTUAL JSON根据预期的JSON结构具有所需的所有键。

注意:实际的JSON仍然可以包含更多不需要预期JSON的密钥。

进一步解释我的笔记……

假设您的预期JSON是:

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

而你的ACTUAL JSON是:

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

上面的函数会告诉你Value键缺失,但是不会提到SomeProp键的任何内容……除非你交换输入参数。