SCH*_*ian 7 c# json.net .net-core system.text.json
是否可以将这样的两个 json 对象与 System.Text.Json?
对象 1
{
id: 1
william: "shakespeare"
}
Run Code Online (Sandbox Code Playgroud)
对象 2
{
william: "dafoe"
foo: "bar"
}
Run Code Online (Sandbox Code Playgroud)
结果对象
{
id: 1
william: "dafoe"
foo: "bar"
}
Run Code Online (Sandbox Code Playgroud)
我可以像这样用 newtonsoft.json 实现它
var obj1 = JObject.Parse(obj1String);
var obj2 = JObject.Parse(obj2String);
obj1.Merge(obj2);
result = settings.ToString();
Run Code Online (Sandbox Code Playgroud)
但是有办法System.Text.Json吗?
ahs*_*han 14
已经存在请求此功能的问题System.Text.Json:https : //github.com/dotnet/corefx/issues/42466
在此期间,您可以编写自己的Merge基于该方法Utf8JsonWriter作为一种解决方法(因为现有的JsonDocument,JsonElementAPI是只读)。
如果您的 JSON 对象仅包含非空的简单/原始值并且属性显示的顺序不是特别重要,那么以下相对简单的代码示例应该适合您:
public static string SimpleObjectMerge(string originalJson, string newContent)
{
var outputBuffer = new ArrayBufferWriter<byte>();
using (JsonDocument jDoc1 = JsonDocument.Parse(originalJson))
using (JsonDocument jDoc2 = JsonDocument.Parse(newContent))
using (var jsonWriter = new Utf8JsonWriter(outputBuffer, new JsonWriterOptions { Indented = true }))
{
JsonElement root1 = jDoc1.RootElement;
JsonElement root2 = jDoc2.RootElement;
// Assuming both JSON strings are single JSON objects (i.e. {...})
Debug.Assert(root1.ValueKind == JsonValueKind.Object);
Debug.Assert(root2.ValueKind == JsonValueKind.Object);
jsonWriter.WriteStartObject();
// Write all the properties of the first document that don't conflict with the second
foreach (JsonProperty property in root1.EnumerateObject())
{
if (!root2.TryGetProperty(property.Name, out _))
{
property.WriteTo(jsonWriter);
}
}
// Write all the properties of the second document (including those that are duplicates which were skipped earlier)
// The property values of the second document completely override the values of the first
foreach (JsonProperty property in root2.EnumerateObject())
{
property.WriteTo(jsonWriter);
}
jsonWriter.WriteEndObject();
}
return Encoding.UTF8.GetString(outputBuffer.WrittenSpan);
}
Run Code Online (Sandbox Code Playgroud)
Newtonsoft.Jsonnull在进行null不覆盖非空属性值的合并时有不同的处理(当有重复时)。我不确定你是否想要这种行为。如果需要,您需要修改上述方法来处理这些null情况。以下是修改:
public static string SimpleObjectMergeWithNullHandling(string originalJson, string newContent)
{
var outputBuffer = new ArrayBufferWriter<byte>();
using (JsonDocument jDoc1 = JsonDocument.Parse(originalJson))
using (JsonDocument jDoc2 = JsonDocument.Parse(newContent))
using (var jsonWriter = new Utf8JsonWriter(outputBuffer, new JsonWriterOptions { Indented = true }))
{
JsonElement root1 = jDoc1.RootElement;
JsonElement root2 = jDoc2.RootElement;
// Assuming both JSON strings are single JSON objects (i.e. {...})
Debug.Assert(root1.ValueKind == JsonValueKind.Object);
Debug.Assert(root2.ValueKind == JsonValueKind.Object);
jsonWriter.WriteStartObject();
// Write all the properties of the first document that don't conflict with the second
// Or if the second is overriding it with null, favor the property in the first.
foreach (JsonProperty property in root1.EnumerateObject())
{
if (!root2.TryGetProperty(property.Name, out JsonElement newValue) || newValue.ValueKind == JsonValueKind.Null)
{
property.WriteTo(jsonWriter);
}
}
// Write all the properties of the second document (including those that are duplicates which were skipped earlier)
// The property values of the second document completely override the values of the first, unless they are null in the second.
foreach (JsonProperty property in root2.EnumerateObject())
{
// Don't write null values, unless they are unique to the second document
if (property.Value.ValueKind != JsonValueKind.Null || !root1.TryGetProperty(property.Name, out _))
{
property.WriteTo(jsonWriter);
}
}
jsonWriter.WriteEndObject();
}
return Encoding.UTF8.GetString(outputBuffer.WrittenSpan);
}
Run Code Online (Sandbox Code Playgroud)
如果您的 JSON 对象可能包含嵌套的 JSON 值,包括其他对象和数组,您也可能希望扩展逻辑来处理它。这样的事情应该工作:
public static string Merge(string originalJson, string newContent)
{
var outputBuffer = new ArrayBufferWriter<byte>();
using (JsonDocument jDoc1 = JsonDocument.Parse(originalJson))
using (JsonDocument jDoc2 = JsonDocument.Parse(newContent))
using (var jsonWriter = new Utf8JsonWriter(outputBuffer, new JsonWriterOptions { Indented = true }))
{
JsonElement root1 = jDoc1.RootElement;
JsonElement root2 = jDoc2.RootElement;
if (root1.ValueKind != JsonValueKind.Array && root1.ValueKind != JsonValueKind.Object)
{
throw new InvalidOperationException($"The original JSON document to merge new content into must be a container type. Instead it is {root1.ValueKind}.");
}
if (root1.ValueKind != root2.ValueKind)
{
return originalJson;
}
if (root1.ValueKind == JsonValueKind.Array)
{
MergeArrays(jsonWriter, root1, root2);
}
else
{
MergeObjects(jsonWriter, root1, root2);
}
}
return Encoding.UTF8.GetString(outputBuffer.WrittenSpan);
}
private static void MergeObjects(Utf8JsonWriter jsonWriter, JsonElement root1, JsonElement root2)
{
Debug.Assert(root1.ValueKind == JsonValueKind.Object);
Debug.Assert(root2.ValueKind == JsonValueKind.Object);
jsonWriter.WriteStartObject();
// Write all the properties of the first document.
// If a property exists in both documents, either:
// * Merge them, if the value kinds match (e.g. both are objects or arrays),
// * Completely override the value of the first with the one from the second, if the value kind mismatches (e.g. one is object, while the other is an array or string),
// * Or favor the value of the first (regardless of what it may be), if the second one is null (i.e. don't override the first).
foreach (JsonProperty property in root1.EnumerateObject())
{
string propertyName = property.Name;
JsonValueKind newValueKind;
if (root2.TryGetProperty(propertyName, out JsonElement newValue) && (newValueKind = newValue.ValueKind) != JsonValueKind.Null)
{
jsonWriter.WritePropertyName(propertyName);
JsonElement originalValue = property.Value;
JsonValueKind originalValueKind = originalValue.ValueKind;
if (newValueKind == JsonValueKind.Object && originalValueKind == JsonValueKind.Object)
{
MergeObjects(jsonWriter, originalValue, newValue); // Recursive call
}
else if (newValueKind == JsonValueKind.Array && originalValueKind == JsonValueKind.Array)
{
MergeArrays(jsonWriter, originalValue, newValue);
}
else
{
newValue.WriteTo(jsonWriter);
}
}
else
{
property.WriteTo(jsonWriter);
}
}
// Write all the properties of the second document that are unique to it.
foreach (JsonProperty property in root2.EnumerateObject())
{
if (!root1.TryGetProperty(property.Name, out _))
{
property.WriteTo(jsonWriter);
}
}
jsonWriter.WriteEndObject();
}
private static void MergeArrays(Utf8JsonWriter jsonWriter, JsonElement root1, JsonElement root2)
{
Debug.Assert(root1.ValueKind == JsonValueKind.Array);
Debug.Assert(root2.ValueKind == JsonValueKind.Array);
jsonWriter.WriteStartArray();
// Write all the elements from both JSON arrays
foreach (JsonElement element in root1.EnumerateArray())
{
element.WriteTo(jsonWriter);
}
foreach (JsonElement element in root2.EnumerateArray())
{
element.WriteTo(jsonWriter);
}
jsonWriter.WriteEndArray();
}
Run Code Online (Sandbox Code Playgroud)
注意:如果性能对您的方案至关重要,则此方法(即使编写缩进)Merge在运行时和分配方面都优于 Newtonsoft.Json 的方法。也就是说,可以根据需要更快地实现实现(例如,不要写缩进、缓存outputBuffer、不接受/返回字符串等)。
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
Intel Core i7-6700 CPU 3.40GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.100-alpha1-015914
[Host] : .NET Core 5.0.0 (CoreCLR 5.0.19.56303, CoreFX 5.0.19.56306), X64 RyuJIT
Job-LACFYV : .NET Core 5.0.0 (CoreCLR 5.0.19.56303, CoreFX 5.0.19.56306), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000
Run Code Online (Sandbox Code Playgroud)
| Method | Mean | Error | StdDev | Median | Min | Max | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------------- |---------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|-------:|------:|----------:|
| MergeNewtonsoft | 29.01 us | 0.570 us | 0.656 us | 28.84 us | 28.13 us | 30.19 us | 1.00 | 7.0801 | 0.0610 | - | 28.98 KB |
| Merge_New | 16.41 us | 0.293 us | 0.274 us | 16.41 us | 16.02 us | 17.00 us | 0.57 | 1.7090 | - | - | 6.99 KB |
Run Code Online (Sandbox Code Playgroud)
dbc*_*dbc 10
从 .Net Core 3.0 开始,JSON 对象的合并不是通过System.Text.Json以下方式实现的:
上没有Merge或Populate方法JsonDocument。
上没有Merge或Populate方法JsonSerializer。
更一般地说,JsonDocument是只读的。它
提供一种机制来检查JSON 值的结构内容,而无需自动实例化数据值。
因此,它不支持以任何方式修改 JSON 值,包括将另一个 JSON 值合并到其中。
目前有一个增强请求来实现可修改的 JSON 文档对象模型:
问题 #39922:可写 Json DOM。它有一个相关的规范Writable JSON Document Object Model (DOM) forSystem.Text.Json . 如果实现此增强功能,JSON 文档的合并将成为可能。您可以添加一个问题,请求功能等同于JContainer.Merge(),作为先决条件链接回问题 #39922。
| 归档时间: |
|
| 查看次数: |
3114 次 |
| 最近记录: |