Skip to content

Instantly share code, notes, and snippets.

@akunzai
Last active March 22, 2021 22:42
Show Gist options
  • Select an option

  • Save akunzai/6f151024d1736c9af2da98455a962259 to your computer and use it in GitHub Desktop.

Select an option

Save akunzai/6f151024d1736c9af2da98455a962259 to your computer and use it in GitHub Desktop.
System.Text.Json.JsonElement extensions
#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