Last active
March 22, 2021 22:42
-
-
Save akunzai/6f151024d1736c9af2da98455a962259 to your computer and use it in GitHub Desktop.
System.Text.Json.JsonElement extensions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #if NETSTANDARD2_1 | |
| using System.Buffers; | |
| #else | |
| using System.IO; | |
| #endif | |
| using System.Linq; | |
| namespace System.Text.Json | |
| { | |
| public static class JsonExtensions | |
| { | |
| // https://stackoverflow.com/questions/58138793/system-text-json-jsonelement-toobject-workaround | |
| public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null) | |
| { | |
| #if NETSTANDARD2_1 | |
| var bufferWriter = new ArrayBufferWriter<byte>(); | |
| using (var writer = new Utf8JsonWriter(bufferWriter)) | |
| { | |
| element.WriteTo(writer); | |
| } | |
| return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options); | |
| #else | |
| using var buffer = new MemoryStream(); | |
| using (var writer = new Utf8JsonWriter(buffer)) | |
| { | |
| element.WriteTo(writer); | |
| } | |
| buffer.Seek(0, SeekOrigin.Begin); | |
| return JsonSerializer.Deserialize<T>(buffer.ToArray(), options); | |
| #endif | |
| } | |
| public static T ToObject<T>(this JsonDocument document, JsonSerializerOptions options = null) | |
| { | |
| if (document == null) | |
| throw new ArgumentNullException(nameof(document)); | |
| return document.RootElement.ToObject<T>(options); | |
| } | |
| // https://github.com/dotnet/runtime/issues/31433 | |
| public static JsonElement Merge(this JsonElement origin, JsonElement target) | |
| { | |
| #if NETSTANDARD2_1 | |
| var buffer = new ArrayBufferWriter<byte>(); | |
| #else | |
| var buffer = new MemoryStream(); | |
| #endif | |
| using (var jsonWriter = new Utf8JsonWriter(buffer, new JsonWriterOptions { Indented = true })) | |
| { | |
| if (origin.ValueKind != JsonValueKind.Array && origin.ValueKind != JsonValueKind.Object) | |
| { | |
| throw new InvalidOperationException($"The original JSON element to merge new content into must be a container type. Instead it is {origin.ValueKind}."); | |
| } | |
| if (origin.ValueKind != target.ValueKind) | |
| { | |
| throw new InvalidOperationException($"The JSON elements to merge must be same container type."); | |
| } | |
| if (origin.ValueKind == JsonValueKind.Array) | |
| { | |
| MergeArrays(jsonWriter, origin, target); | |
| } | |
| else | |
| { | |
| MergeObjects(jsonWriter, origin, target); | |
| } | |
| } | |
| #if NETSTANDARD2_1 | |
| var doc = JsonDocument.Parse(buffer.WrittenMemory); | |
| #else | |
| buffer.Seek(0, SeekOrigin.Begin); | |
| var doc = JsonDocument.Parse(buffer.ToArray()); | |
| #endif | |
| return doc.RootElement; | |
| } | |
| private static void MergeObjects(Utf8JsonWriter jsonWriter, JsonElement origin, JsonElement target) | |
| { | |
| if (origin.ValueKind != JsonValueKind.Object) | |
| { | |
| throw new ArgumentException("The origin JSON element must be Object container", nameof(origin)); | |
| } | |
| if (target.ValueKind != JsonValueKind.Object) | |
| { | |
| throw new ArgumentException("The target JSON element must be Object container", nameof(target)); | |
| } | |
| jsonWriter.WriteStartObject(); | |
| foreach (var property in origin.EnumerateObject()) | |
| { | |
| var propertyName = property.Name; | |
| JsonValueKind newValueKind; | |
| if (target.TryGetProperty(propertyName, out var newValue) && (newValueKind = newValue.ValueKind) != JsonValueKind.Null) | |
| { | |
| jsonWriter.WritePropertyName(propertyName); | |
| var originalValue = property.Value; | |
| var originalValueKind = originalValue.ValueKind; | |
| switch (newValueKind) | |
| { | |
| case JsonValueKind.Object when originalValueKind == JsonValueKind.Object: | |
| MergeObjects(jsonWriter, originalValue, newValue); | |
| break; | |
| case JsonValueKind.Array when originalValueKind == JsonValueKind.Array: | |
| MergeArrays(jsonWriter, originalValue, newValue); | |
| break; | |
| default: | |
| newValue.WriteTo(jsonWriter); | |
| break; | |
| } | |
| } | |
| else | |
| { | |
| property.WriteTo(jsonWriter); | |
| } | |
| } | |
| foreach (var property in target.EnumerateObject().Where(property => !origin.TryGetProperty(property.Name, out _))) | |
| { | |
| property.WriteTo(jsonWriter); | |
| } | |
| jsonWriter.WriteEndObject(); | |
| } | |
| private static void MergeArrays(Utf8JsonWriter jsonWriter, JsonElement origin, JsonElement target) | |
| { | |
| if (origin.ValueKind != JsonValueKind.Array) | |
| { | |
| throw new ArgumentException("The origin JSON element must be Array container", nameof(origin)); | |
| } | |
| if (target.ValueKind != JsonValueKind.Array) | |
| { | |
| throw new ArgumentException("The target JSON element must be Array container", nameof(target)); | |
| } | |
| jsonWriter.WriteStartArray(); | |
| foreach (var element in origin.EnumerateArray()) | |
| { | |
| element.WriteTo(jsonWriter); | |
| } | |
| foreach (var element in target.EnumerateArray()) | |
| { | |
| element.WriteTo(jsonWriter); | |
| } | |
| jsonWriter.WriteEndArray(); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment