我想获得使用Newtonsoft进行比较时不匹配的JSON部分列表。
我有这个代码比较:
JObject xpctJSON = JObject.Parse(expectedJSON);
JObject actJSON = JObject.Parse(actualJSON);
bool res = JToken.DeepEquals(xpctJSON, actJSON);
但是找不到任何可以返回差异的东西。
答案 0 :(得分:16)
只是为了帮助将来的查询。我遇到了一个不错的json diff工具。它对于json结构的diff / patch非常完美:
jsondiffpatch.net 还有一个nuget包。
用法很简单。
var jdp = new JsonDiffPatch();
JToken diffResult = jdp.Diff(leftJson, rightJson);
答案 1 :(得分:14)
这是我写的递归版本。您使用两个JObject调用CompareObjects,它返回差异列表。您使用两个JArrays调用CompareArrays并比较数组。数组和对象可以互相嵌套。
UPDATE :@nttakr在下面的评论中指出,此方法实际上是一种偏差算法。它只会告诉您与源列表的不同之处。如果源中不存在键但目标列表中存在该键,则将忽略该差异。这是我的测试要求的设计。这使您可以测试所需的项目,而无需在完成比较之前将其从目标中删除。
/// <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;
}
答案 2 :(得分:12)
这是一个相对古老的问题,但是发布了解决此问题的可能方法之一,假设您想要的结果恰好是哪些属性值已更改
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");
}
注意:尚未对非常深层次的测试进行测试。
答案 3 :(得分:3)
请注意以下库:
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
我不完全确定我正确理解你的问题。 我假设您正在尝试确定实际JSON中缺少哪些键。
如果您只对缺少的KEYS感兴趣,以下代码将对您有所帮助,如果没有,请提供您尝试识别的差异类型的示例。
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;
}
注意,如果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键的任何内容......除非你交换输入参数。
答案 4 :(得分:2)
这几乎是一个旧线程,但是由于我几个月前来这里寻找一种可靠的工具,却找不到一个,我已经编写了自己的工具,如果您正在寻找类似的工具,则可以使用它以下:
{
"name":"John",
"age":30,
"cars": {
"car1":"Ford",
"car2":"BMW",
"car3":"Fiat"
}
}
{
"name":"John",
"cars": {
"car1":"Ford",
"car2":"BMW",
"car3":"Audi",
"car4":"Jaguar"
}
}
var j1 = JToken.Parse(Read(json1));
var j2 = JToken.Parse(Read(json2));
var diff = JsonDifferentiator.Differentiate(j1,j2);
{
"-age": 30,
"*cars": {
"*car3": "Fiat",
"+car4": "Jaguar"
}
}
请随时签出源代码并查看测试,欢迎您提供反馈意见:)
答案 5 :(得分:1)
我的解决方案基于先前答案中的思想:
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)
{
diff[k] = FindDiff(current[k], model[k]);
}
}
break;
case JTokenType.Array:
{
var current = Current as JArray;
var model = Model as JArray;
diff["+"] = new JArray(current.Except(model));
diff["-"] = new JArray(model.Except(current));
}
break;
default:
diff["+"] = Current;
diff["-"] = Model;
break;
}
return diff;
}
答案 6 :(得分:0)
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);
}
}
答案 7 :(得分:0)
这里没有答案是我真正需要的。
这是一个为比较的两个对象中的每个对象返回JObject的方法。 JObjects仅包含不同的属性。这对于扫描对实体的更改并存储快照前后(序列化JObject)非常有用。
NB:更改扫描仅在顶级属性上进行。
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);
}
答案 8 :(得分:0)
我已经转换为更精确的对象阵列
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;
}