diff --git a/src/Elastic.Transport/Components/Serialization/IJsonSerializerOptionsProvider.cs b/src/Elastic.Transport/Components/Serialization/IJsonSerializerOptionsProvider.cs new file mode 100644 index 0000000..10ec20e --- /dev/null +++ b/src/Elastic.Transport/Components/Serialization/IJsonSerializerOptionsProvider.cs @@ -0,0 +1,61 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Elastic.Transport; + +/// +/// Provides an instance of to +/// +public interface IJsonSerializerOptionsProvider +{ + /// + JsonSerializerOptions CreateJsonSerializerOptions(); +} + +/// +/// Default implementation of specialized in providing more converters and +/// altering the shared used by and its derived classes +/// +public class TransportSerializerOptionsProvider : IJsonSerializerOptionsProvider +{ + private readonly IReadOnlyCollection? _bakedInConverters; + private readonly IReadOnlyCollection? _userProvidedConverters; + private readonly Action? _mutateOptions; + + /// + public JsonSerializerOptions? CreateJsonSerializerOptions() + { + var options = new JsonSerializerOptions(); + + foreach (var converter in _bakedInConverters ?? []) + options.Converters.Add(converter); + + foreach (var converter in _userProvidedConverters ?? []) + options.Converters.Add(converter); + + _mutateOptions?.Invoke(options); + + return options; + } + + /// + public TransportSerializerOptionsProvider() { } + + /// + public TransportSerializerOptionsProvider( + IReadOnlyCollection bakedInConverters, + IReadOnlyCollection? userProvidedConverters, + Action? mutateOptions = null + ) + { + _bakedInConverters = bakedInConverters; + _userProvidedConverters = userProvidedConverters; + _mutateOptions = mutateOptions; + } +} diff --git a/src/Elastic.Transport/Components/Serialization/LowLevelRequestResponseSerializer.cs b/src/Elastic.Transport/Components/Serialization/LowLevelRequestResponseSerializer.cs index 36602fc..183d4cf 100644 --- a/src/Elastic.Transport/Components/Serialization/LowLevelRequestResponseSerializer.cs +++ b/src/Elastic.Transport/Components/Serialization/LowLevelRequestResponseSerializer.cs @@ -2,43 +2,23 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information -using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading; -using System.Threading.Tasks; -using Elastic.Transport.Extensions; -using static Elastic.Transport.SerializationFormatting; namespace Elastic.Transport; /// -/// Default implementation for . This uses from System.Text.Json. +/// Default low level request/response-serializer implementation for which serializes using +/// the Microsoft System.Text.Json library /// -internal sealed class LowLevelRequestResponseSerializer : Serializer +internal sealed class LowLevelRequestResponseSerializer : SystemTextJsonSerializer { /// /// Provides a static reusable reference to an instance of to promote reuse. /// internal static readonly LowLevelRequestResponseSerializer Instance = new(); - private readonly Lazy _indented; - private readonly Lazy _none; - - private IReadOnlyCollection AdditionalConverters { get; } - - private IList BakedInConverters { get; } = new List - { - new ExceptionConverter(), - new ErrorCauseConverter(), - new ErrorConverter(), - new DynamicDictionaryConverter() - }; - /// > public LowLevelRequestResponseSerializer() : this(null) { } @@ -46,94 +26,12 @@ public LowLevelRequestResponseSerializer() : this(null) { } /// > /// /// Add more default converters onto being used - public LowLevelRequestResponseSerializer(IEnumerable? converters) - { - AdditionalConverters = converters != null - ? new ReadOnlyCollection(converters.ToList()) - : EmptyReadOnly.Collection; - _indented = new Lazy(() => CreateSerializerOptions(Indented)); - _none = new Lazy(() => CreateSerializerOptions(None)); - } - - /// - /// Creates used for serialization. - /// Override on a derived serializer to change serialization. - /// - public JsonSerializerOptions CreateSerializerOptions(SerializationFormatting formatting) - { - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - WriteIndented = formatting == Indented, - }; - foreach (var converter in BakedInConverters) - options.Converters.Add(converter); - foreach (var converter in AdditionalConverters) - options.Converters.Add(converter); - - return options; - - } - - private static bool TryReturnDefault(Stream? stream, out T deserialize) - { - deserialize = default; - return stream == null || stream == Stream.Null || (stream.CanSeek && stream.Length == 0); - } - - private JsonSerializerOptions GetFormatting(SerializationFormatting formatting) => formatting == None ? _none.Value : _indented.Value; - - /// > - public override object Deserialize(Type type, Stream stream) - { - if (TryReturnDefault(stream, out object deserialize)) return deserialize; - - return JsonSerializer.Deserialize(stream, type, _none.Value)!; - } - - /// > - public override T Deserialize(Stream stream) - { - if (TryReturnDefault(stream, out T deserialize)) return deserialize; - - return JsonSerializer.Deserialize(stream, _none.Value); - } - - /// > - public override void Serialize(T data, Stream stream, SerializationFormatting formatting = None) - { - using var writer = new Utf8JsonWriter(stream); - if (data == null) - JsonSerializer.Serialize(writer, null, typeof(object), GetFormatting(formatting)); - //TODO validate if we can avoid boxing by checking if data is typeof(object) - else - JsonSerializer.Serialize(writer, data, data.GetType(), GetFormatting(formatting)); - } - - /// > - public override async Task SerializeAsync(T data, Stream stream, SerializationFormatting formatting = None, - CancellationToken cancellationToken = default - ) - { - if (data == null) - await JsonSerializer.SerializeAsync(stream, null, typeof(object), GetFormatting(formatting), cancellationToken).ConfigureAwait(false); - else - await JsonSerializer.SerializeAsync(stream, data, data.GetType(), GetFormatting(formatting), cancellationToken).ConfigureAwait(false); - } - - /// > - public override ValueTask DeserializeAsync(Type type, Stream stream, CancellationToken cancellationToken = default) - { - if (TryReturnDefault(stream, out object deserialize)) return new ValueTask(deserialize); - - return JsonSerializer.DeserializeAsync(stream, type, _none.Value, cancellationToken); - } - - /// > - public override ValueTask DeserializeAsync(Stream stream, CancellationToken cancellationToken = default) - { - if (TryReturnDefault(stream, out T deserialize)) return new ValueTask(deserialize); + public LowLevelRequestResponseSerializer(IReadOnlyCollection? converters) + : base(new TransportSerializerOptionsProvider([ + new ExceptionConverter(), + new ErrorCauseConverter(), + new ErrorConverter(), + new DynamicDictionaryConverter() + ], converters, options => { options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; })) { } - return JsonSerializer.DeserializeAsync(stream, _none.Value, cancellationToken); - } } diff --git a/src/Elastic.Transport/Components/Serialization/Serializer.cs b/src/Elastic.Transport/Components/Serialization/Serializer.cs index 04e3744..f0a58c4 100644 --- a/src/Elastic.Transport/Components/Serialization/Serializer.cs +++ b/src/Elastic.Transport/Components/Serialization/Serializer.cs @@ -32,6 +32,8 @@ public abstract class Serializer /// public abstract ValueTask DeserializeAsync(Stream stream, CancellationToken cancellationToken = default); + // TODO: Overloads for (object?, Type) inputs + /// /// Serialize an instance of to using . /// diff --git a/src/Elastic.Transport/Components/Serialization/SystemTextJsonSerializer.cs b/src/Elastic.Transport/Components/Serialization/SystemTextJsonSerializer.cs new file mode 100644 index 0000000..0a713dd --- /dev/null +++ b/src/Elastic.Transport/Components/Serialization/SystemTextJsonSerializer.cs @@ -0,0 +1,102 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace Elastic.Transport; + +/// +/// An abstract implementation of a transport which serializes using the Microsoft +/// System.Text.Json library. +/// +public abstract class SystemTextJsonSerializer : Serializer +{ + private readonly JsonSerializerOptions? _options; + private readonly JsonSerializerOptions? _indentedOptions; + + /// + /// An abstract implementation of a transport which serializes using the Microsoft + /// System.Text.Json library. + /// + protected SystemTextJsonSerializer(IJsonSerializerOptionsProvider? provider = null) + { + provider ??= new TransportSerializerOptionsProvider(); + _options = provider.CreateJsonSerializerOptions(); + _indentedOptions = new JsonSerializerOptions(_options) + { + WriteIndented = true + }; + } + + #region Serializer + + /// + public override T Deserialize(Stream stream) + { + if (TryReturnDefault(stream, out T deserialize)) + return deserialize; + + return JsonSerializer.Deserialize(stream, GetJsonSerializerOptions()); + } + + /// + public override object? Deserialize(Type type, Stream stream) + { + if (TryReturnDefault(stream, out object deserialize)) + return deserialize; + + return JsonSerializer.Deserialize(stream, type, GetJsonSerializerOptions()); + } + + /// + public override ValueTask DeserializeAsync(Stream stream, CancellationToken cancellationToken = default) + { + if (TryReturnDefault(stream, out T deserialize)) + return new ValueTask(deserialize); + + return JsonSerializer.DeserializeAsync(stream, GetJsonSerializerOptions(), cancellationToken); + } + + /// + public override ValueTask DeserializeAsync(Type type, Stream stream, CancellationToken cancellationToken = default) + { + if (TryReturnDefault(stream, out object deserialize)) + return new ValueTask(deserialize); + + return JsonSerializer.DeserializeAsync(stream, type, GetJsonSerializerOptions(), cancellationToken); + } + + /// + public override void Serialize(T data, Stream writableStream, + SerializationFormatting formatting = SerializationFormatting.None) => + JsonSerializer.Serialize(writableStream, data, GetJsonSerializerOptions(formatting)); + + /// + public override Task SerializeAsync(T data, Stream stream, + SerializationFormatting formatting = SerializationFormatting.None, + CancellationToken cancellationToken = default) => + JsonSerializer.SerializeAsync(stream, data, GetJsonSerializerOptions(formatting), cancellationToken); + + #endregion Serializer + + /// + /// Returns the for this serializer, based on the given . + /// + /// The serialization formatting. + /// The requested or null, if the serializer is not initialized yet. + protected internal JsonSerializerOptions? GetJsonSerializerOptions(SerializationFormatting formatting = SerializationFormatting.None) => + formatting is SerializationFormatting.None ? _options : _indentedOptions; + + private static bool TryReturnDefault(Stream? stream, out T deserialize) + { + deserialize = default; + return (stream is null) || stream == Stream.Null || (stream.CanSeek && stream.Length == 0); + } +} diff --git a/src/Elastic.Transport/Components/Serialization/TransportSerializerExtensions.cs b/src/Elastic.Transport/Components/Serialization/TransportSerializerExtensions.cs index 68c0c8e..cfc6a87 100644 --- a/src/Elastic.Transport/Components/Serialization/TransportSerializerExtensions.cs +++ b/src/Elastic.Transport/Components/Serialization/TransportSerializerExtensions.cs @@ -2,6 +2,12 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.IO; +using System.Text.Json; +using System; +using System.Text; +using System.Text.Json.Nodes; + namespace Elastic.Transport.Extensions; /// @@ -12,25 +18,69 @@ public static class TransportSerializerExtensions /// /// Extension method that serializes an instance of to a byte array. /// + /// The type of the data to be serialized. + /// + /// + /// public static byte[] SerializeToBytes( this Serializer serializer, - T data, + T? data, SerializationFormatting formatting = SerializationFormatting.None) => SerializeToBytes(serializer, data, TransportConfiguration.DefaultMemoryStreamFactory, formatting); /// /// Extension method that serializes an instance of to a byte array. /// + /// The type of the data to be serialized. + /// /// /// /// A factory yielding MemoryStream instances, defaults to /// that yields memory streams backed by pooled byte arrays. /// - /// /// public static byte[] SerializeToBytes( this Serializer serializer, - T data, + T? data, + MemoryStreamFactory? memoryStreamFactory = null, + SerializationFormatting formatting = SerializationFormatting.None + ) + { + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + serializer.Serialize(data, ms, formatting); + return ms.ToArray(); + } + + /// + /// Extension method that serializes the given to a byte array. + /// + /// + /// + /// The type of the data to serialize. + /// + public static byte[] SerializeToBytes( + this Serializer serializer, + object? data, + Type type, + SerializationFormatting formatting = SerializationFormatting.None) => + SerializeToBytes(serializer, data, type, TransportConfiguration.DefaultMemoryStreamFactory, formatting); + + /// + /// Extension method that serializes the given to a byte array. + /// + /// + /// + /// The type of the data to serialize. + /// + /// A factory yielding MemoryStream instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// + public static byte[] SerializeToBytes( + this Serializer serializer, + object? data, + Type type, MemoryStreamFactory? memoryStreamFactory = null, SerializationFormatting formatting = SerializationFormatting.None ) @@ -44,32 +94,545 @@ public static byte[] SerializeToBytes( /// /// Extension method that serializes an instance of to a string. /// + /// The type of the data to be serialized. + /// + /// The data to serialize. + /// public static string SerializeToString( this Serializer serializer, - T data, + T? data, SerializationFormatting formatting = SerializationFormatting.None) => SerializeToString(serializer, data, TransportConfiguration.DefaultMemoryStreamFactory, formatting); /// /// Extension method that serializes an instance of to a string. /// - /// + /// The type of the data to be serialized. + /// + /// The data to serialize. /// /// A factory yielding MemoryStream instances, defaults to /// that yields memory streams backed by pooled byte arrays. /// - /// /// public static string SerializeToString( this Serializer serializer, - T data, + T? data, MemoryStreamFactory? memoryStreamFactory = null, SerializationFormatting formatting = SerializationFormatting.None ) { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // serialize straight into string. + return JsonSerializer.Serialize(data, stjSerializer.GetJsonSerializerOptions(formatting)); + } + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; using var ms = memoryStreamFactory.Create(); + serializer.Serialize(data, ms, formatting); + return ms.Utf8String(); } + + /// + /// Extension method that serializes the given to a string. + /// + /// + /// The data to serialize. + /// The type of the data to serialize. + /// + public static string SerializeToString( + this Serializer serializer, + object? data, + Type type, + SerializationFormatting formatting = SerializationFormatting.None) => + SerializeToString(serializer, data, type, TransportConfiguration.DefaultMemoryStreamFactory, formatting); + + /// + /// Extension method that serializes the given to a string. + /// + /// + /// The data to serialize. + /// The type of the data to serialize. + /// + /// A factory yielding MemoryStream instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// + public static string SerializeToString( + this Serializer serializer, + object? data, + Type type, + MemoryStreamFactory? memoryStreamFactory = null, + SerializationFormatting formatting = SerializationFormatting.None + ) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // serialize straight into string. + return JsonSerializer.Serialize(data, type, stjSerializer.GetJsonSerializerOptions(formatting)); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + serializer.Serialize(data, ms, formatting); + + return ms.Utf8String(); + } + + #region STJ Extensions + + /// + /// Extension method that writes the serialized representation of an instance of to a + /// . + /// + /// The type of the data to be serialized. + /// + /// The data to serialize. + /// The destination . + /// + public static void Serialize( + this Serializer serializer, + T? data, + Utf8JsonWriter writer, + SerializationFormatting formatting = SerializationFormatting.None + ) => Serialize(serializer, data, writer, TransportConfiguration.DefaultMemoryStreamFactory, formatting); + + /// + /// Extension method that writes the serialized representation of an instance of to a + /// . + /// + /// The type of the data to be serialized. + /// + /// The data to serialize. + /// The destination . + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// + public static void Serialize( + this Serializer serializer, + T? data, + Utf8JsonWriter writer, + MemoryStreamFactory? memoryStreamFactory = null, + SerializationFormatting formatting = SerializationFormatting.None) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // serialize straight into the writer. + JsonSerializer.Serialize(writer, data, stjSerializer.GetJsonSerializerOptions(formatting)); + return; + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + serializer.Serialize(data, ms); + ms.Position = 0; + +#if NET6_0_OR_GREATER + writer.WriteRawValue(ms.GetBuffer().AsSpan()[..(int)ms.Length], true); +#else + using var document = JsonDocument.Parse(ms); + document.RootElement.WriteTo(writer); +#endif + } + + /// + /// Extension method that writes the serialized representation of the given to a + /// . + /// + /// + /// The data to serialize. + /// The type of the data to serialize. + /// The destination . + /// + public static void Serialize( + this Serializer serializer, + object? data, + Type type, + Utf8JsonWriter writer, + SerializationFormatting formatting = SerializationFormatting.None + ) => Serialize(serializer, data, type, writer, TransportConfiguration.DefaultMemoryStreamFactory, formatting); + + /// + /// Extension method that writes the serialized representation of the given to a + /// . + /// + /// + /// The data to serialize. + /// The type of the data to serialize. + /// The destination . + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// + public static void Serialize( + this Serializer serializer, + object? data, + Type type, + Utf8JsonWriter writer, + MemoryStreamFactory? memoryStreamFactory = null, + SerializationFormatting formatting = SerializationFormatting.None) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // serialize straight into the writer. + JsonSerializer.Serialize(writer, data, type, stjSerializer.GetJsonSerializerOptions(formatting)); + return; + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + serializer.Serialize(data, ms); + ms.Position = 0; + +#if NET6_0_OR_GREATER + writer.WriteRawValue(ms.GetBuffer().AsSpan()[..(int)ms.Length], true); +#else + using var document = JsonDocument.Parse(ms); + document.RootElement.WriteTo(writer); +#endif + } + + /// + /// Extension method that deserializes from a UTF8 . + /// + /// The type of the data to be deserialized. + /// + /// The source that contains the UTF8 encoded JSON string. + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static T? Deserialize( + this Serializer serializer, + ReadOnlySpan span, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the span. + return JsonSerializer.Deserialize(span, stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(span.ToArray()); + + return serializer.Deserialize(ms); + } + + /// + /// Extension method that deserializes from a UTF8 . + /// + /// + /// The source that contains the UTF8 encoded JSON. + /// The type of the data to be deserialized. + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static object? Deserialize( + this Serializer serializer, + ReadOnlySpan span, + Type type, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the span. + return JsonSerializer.Deserialize(span, type, stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(span.ToArray()); + + return serializer.Deserialize(type, ms); + } + + /// + /// Extension method that deserializes from a UTF8 . + /// + /// The type of the data to be deserialized. + /// + /// The source that contains the UTF8 encoded JSON string. + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static T? Deserialize( + this Serializer serializer, + ReadOnlySpan span, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the span. + return JsonSerializer.Deserialize(span, stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(Encoding.UTF8.GetBytes(span.ToArray())); + + return serializer.Deserialize(ms); + } + + /// + /// Extension method that deserializes from a UTF8 . + /// + /// + /// The source that contains the UTF8 encoded JSON. + /// The type of the data to be deserialized. + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static object? Deserialize( + this Serializer serializer, + ReadOnlySpan span, + Type type, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the span. + return JsonSerializer.Deserialize(span, type, stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(Encoding.UTF8.GetBytes(span.ToArray())); + + return serializer.Deserialize(type, ms); + } + + /// + /// Extension method that deserializes from a given . + /// + /// The type of the data to be deserialized. + /// + /// The source + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static T? Deserialize( + this Serializer serializer, + ref Utf8JsonReader reader, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the reader. + return JsonSerializer.Deserialize(ref reader, stjSerializer.GetJsonSerializerOptions()); + } + + using var jsonDoc = JsonSerializer.Deserialize(ref reader); + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + var writer = new Utf8JsonWriter(ms); + jsonDoc.WriteTo(writer); + writer.Flush(); + ms.Position = 0; + + return serializer.Deserialize(ms); + } + + /// + /// Extension method that deserializes from a given . + /// + /// + /// The source + /// The type of the data to be deserialized. + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static object? Deserialize( + this Serializer serializer, + ref Utf8JsonReader reader, + Type type, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the reader. + return JsonSerializer.Deserialize(ref reader, type, stjSerializer.GetJsonSerializerOptions()); + } + + using var jsonDoc = JsonSerializer.Deserialize(ref reader); + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + var writer = new Utf8JsonWriter(ms); + jsonDoc.WriteTo(writer); + writer.Flush(); + ms.Position = 0; + + return serializer.Deserialize(type, ms); + } + + /// + /// Extension method that deserializes from a given . + /// + /// The type of the data to be deserialized. + /// + /// The source + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static T? Deserialize( + this Serializer serializer, + JsonNode node, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the node. + return node.Deserialize(stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + using var writer = new Utf8JsonWriter(ms); + node.WriteTo(writer); + writer.Flush(); + ms.Position = 0; + + return serializer.Deserialize(ms); + } + + /// + /// Extension method that deserializes from a given . + /// + /// + /// The source + /// The type of the data to be deserialized. + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static object? Deserialize( + this Serializer serializer, + JsonNode node, + Type type, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the node. + return node.Deserialize(type, stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + using var writer = new Utf8JsonWriter(ms); + node.WriteTo(writer); + writer.Flush(); + ms.Position = 0; + + return serializer.Deserialize(type, ms); + } + + /// + /// Extension method that deserializes from a given . + /// + /// The type of the data to be deserialized. + /// + /// The source + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static T? Deserialize( + this Serializer serializer, + JsonElement node, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the node. + return node.Deserialize(stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + using var writer = new Utf8JsonWriter(ms); + node.WriteTo(writer); + writer.Flush(); + ms.Position = 0; + + return serializer.Deserialize(ms); + } + + /// + /// Extension method that deserializes from a given . + /// + /// + /// The source + /// The type of the data to be deserialized. + /// + /// A factory yielding instances, defaults to + /// that yields memory streams backed by pooled byte arrays. + /// + /// The deserialized data. + public static object? Deserialize( + this Serializer serializer, + JsonElement node, + Type type, + MemoryStreamFactory? memoryStreamFactory = null) + { + if (serializer is SystemTextJsonSerializer stjSerializer) + { + // When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and + // deserialize straight from the node. + return node.Deserialize(type, stjSerializer.GetJsonSerializerOptions()); + } + + memoryStreamFactory ??= TransportConfiguration.DefaultMemoryStreamFactory; + using var ms = memoryStreamFactory.Create(); + + using var writer = new Utf8JsonWriter(ms); + node.WriteTo(writer); + writer.Flush(); + ms.Position = 0; + + return serializer.Deserialize(type, ms); + } + + #endregion STJ Extensions }