【发布时间】:2023-02-26 10:29:06
【问题描述】:
您可能知道 C# Except() 方法,该方法从第一个集合中删除第二个集合中包含的元素。我必须为 JSON 编写类比方法。
那就是我所做的:
public static JsonDocument Except(this JsonDocument firstJson, JsonDocument secondJson)
{
if (firstJson.RootElement.ValueKind != secondJson.RootElement.ValueKind)
throw new JsonException($"The '{nameof(firstJson)}' and '{nameof(secondJson)}' must be the same kind of value");
switch (firstJson.RootElement.ValueKind)
{
case JsonValueKind.Object:
var result = JsonNode.Parse("{}")!;
var firstJsonProperties = firstJson.RootElement.EnumerateObject();
foreach (var firstJsonProperty in firstJsonProperties)
{
if (!secondJson.RootElement.TryGetProperty(firstJsonProperty.Name, out JsonElement secondJsonPropertyValue)
|| secondJsonPropertyValue.ValueKind != firstJsonProperty.Value.ValueKind)
{
result[firstJsonProperty.Name] = firstJsonProperty.Value.AsNode();
continue;
}
else if (firstJsonProperty.Value.ValueKind == JsonValueKind.Object)
{
var objectExceptionResult = Except(firstJsonProperty.Value.ToJsonDocument(), secondJsonPropertyValue.ToJsonDocument());
if (objectExceptionResult.RootElement.AsNode()!.AsObject().Any())
result[firstJsonProperty.Name] = objectExceptionResult.RootElement.AsNode();
}
else if (firstJsonProperty.Value.ValueKind == JsonValueKind.Array)
{
var arrayExceptionResult = Except(firstJsonProperty.Value.ToJsonDocument(), secondJsonPropertyValue.ToJsonDocument());
if (arrayExceptionResult.RootElement.AsNode()!.AsArray().Any())
result[firstJsonProperty.Name] = arrayExceptionResult.RootElement.AsNode();
}
}
return result.ToJsonDocument();
case JsonValueKind.Array:
var result2 = new JsonArray();
var firstJsonElements = firstJson.RootElement.EnumerateArray();
var secondJsonElements = secondJson.RootElement.EnumerateArray();
foreach (var firstJsonElement in firstJsonElements)
{
foreach (var secondJsonElement in secondJsonElements)
{
if (firstJsonElement.ValueKind != secondJsonElement.ValueKind)
continue;
if (firstJsonElement.ValueKind == JsonValueKind.Object || firstJsonElement.ValueKind == JsonValueKind.Array)
{
var exceptionResult = Except(firstJsonElement.ToJsonDocument(), secondJsonElement.ToJsonDocument());
if (!firstJsonElement.IsEquivalentTo(exceptionResult.RootElement))
{
if (exceptionResult.RootElement.AsNode()!.AsObject().Any())
result2.Add(exceptionResult);
break;
}
else if (secondJsonElement.IsEquivalentTo(secondJsonElements.Last()))
{
result2.Add(firstJsonElement);
}
}
}
if (firstJsonElement.ValueKind != JsonValueKind.Object && firstJsonElement.ValueKind != JsonValueKind.Array
&& !secondJsonElements.Any(p => p.ToString() == firstJsonElement.ToString()))
result2.Add(firstJsonElement);
}
return result2.ToJsonDocument();
default:
if (!firstJson.RootElement.IsEquivalentTo(secondJson.RootElement))
return firstJson;
break;
}
return firstJson;
}
这段代码并不漂亮。但更糟糕的事情发生了。它有时不起作用。
对于像这样的简单 JSON 数据:
var firstJson = JsonDocument.Parse(@"{
""x"":""haha"",
""a"":{
""z"":1,
""b"":3
},
""haff"": [
1,
2,
{
""a"": 4,
""b"": 5
}
]
}");
var secondJson = JsonDocument.Parse(@"{
""x"": 1024,
""b"":""afa"",
""a"":{
""z"":3,
""a"":4
},
""haff"": [
1,
2,
{
""a"": 5
}
]
}");
它工作得很好。但是当第一个和第二个 JSON 都是数组并且第一个的元素少于第二个时,就会发生不好的事情。然后并不是所有适当的元素都从第一个 JSON 中删除。
我真的不知道为什么会这样。你知道这段代码出了什么问题吗?或者,也许您知道可以使用提供此功能的 NuGet 包?
如果您需要更多详细信息,请评论我的问题。
注意:我在此代码中使用 Json.More.Net NuGet 包。
通常,代码应:
- 如果该属性存储简单结构(字符串、整数等),那么它是键值对,如果它也包含在第二个 JSON 中,则应删除该属性
- 如果属性存储数组,则将从数组中删除所有也包含在第二个 JSON 中的适当数组中的元素。
- 如果属性存储对象,则将从该对象中删除属性,这些属性也包含在第二个 JSON 中的适当对象中。
- 如果从数组或对象中删除所有数据,它也应删除整个对象或数组。
这是调用
Except(firstJson, secondJson)的结果(上面定义的变量):{ "x":"haha", "a":{ "b":3 }, "haff":[ { "b":5 } ] }当调用
Except(secondJson, firstJson)(所以我用 secondJson 交换了 firstJson)时,结果将如下所示:{ "x":1024, "b":"afa", "a":{ "a":4 } }看起来很简单,但请记住,数组可以包含对象,而对象又包含另一个数组,数组又包含另一个对象等。
【问题讨论】:
-
没有人会阅读你的代码。这个资源太多了。如果您需要一些代码审查,这里有一个专门的论坛。因此,除了数组或对象之外,您的问题还不清楚您要做什么?整个对象还是只有属性?
-
我添加了关于代码应该做什么的描述。顺便说一句,我在 codereview 论坛上,我的问题被删除了,因为它不是完全有效的代码。
-
感谢 jsons,但我还是不明白你想要什么。你能发布你想要获得的最终 json 吗?
-
您是在比较属性名称还是值?
-
我已经编辑了我的问题。我正在比较属性名称和值。如果属性名称匹配,那么我检查该属性存储的数据类型是否也匹配。