Skip to content

Commit 36f1d47

Browse files
authored
Add SystemTextJsonSerializer.SupportsFastPath method (#154)
The `DefaultRequestResponseSerializer` implementation in the Elasticsearch client (based on `SystemTextJsonSerializer`) overrides the `Deserialize`/`Serialize` methods to drive special serialization, if the type implements `IStreamSerializable`. This is done to support e.g. NDJSON request/response bodies. Fast-path (de-)serialization currently incorrectly bypasses this special handling. This PR provides a way to selectively disable the fast-path (de-)serialization for specific types.
1 parent 7634537 commit 36f1d47

File tree

3 files changed

+45
-20
lines changed

3 files changed

+45
-20
lines changed

src/Elastic.Transport/Components/Serialization/SystemTextJsonSerializer.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,31 @@ public override Task SerializeAsync<T>(T data, Stream stream,
8484

8585
#endregion Serializer
8686

87+
/// <summary>
88+
/// Override to (dis-)allow fast-path (de-)serialization for specific types.
89+
/// </summary>
90+
/// <param name="type">The <see cref="Type"/> that is being (de-)serialized.</param>
91+
/// <returns>
92+
/// <see langword="true"/> if the given <paramref name="type"/> supports fast-path (de-)serialization or
93+
/// <see langword="false"/>, if not.
94+
/// </returns>
95+
/// <remarks>
96+
/// <para>
97+
/// Most extension methods in <see cref="Extensions.TransportSerializerExtensions"/> will prefer fast-path (de-)serialization, when
98+
/// used with <see cref="SystemTextJsonSerializer"/> based serializer implementations.
99+
/// Fast-path (de-)serialization bypasses the <see cref="Deserialize"/>, <see cref="Deserialize{T}"/>, <see cref="Serialize{T}"/>
100+
/// methods and directly uses the <see cref="JsonSerializer"/> API instead.
101+
/// </para>
102+
/// <para>
103+
/// In some cases, when the concrete <see cref="SystemTextJsonSerializer"/> based serializer implementation overrides one or more of
104+
/// the previously named methods, the default fast-path behavior is probably undesired as it would prevent the user defined code in
105+
/// the overwritten methods from being executed.
106+
/// The <see cref="SupportsFastPath"/> method can be used to either completely disable fast-path (de-)serialization or to selectively
107+
/// allow it for specific types only.
108+
/// </para>
109+
/// </remarks>
110+
protected internal virtual bool SupportsFastPath(Type type) => true;
111+
87112
/// <summary>
88113
/// Returns the <see cref="JsonSerializerOptions"/> for this serializer, based on the given <paramref name="formatting"/>.
89114
/// </summary>

src/Elastic.Transport/Components/Serialization/TransportSerializerExtensions.cs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public static byte[] SerializeToBytes<T>(
4747
SerializationFormatting formatting = SerializationFormatting.None
4848
)
4949
{
50-
if (serializer is SystemTextJsonSerializer stjSerializer)
50+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
5151
{
5252
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations.
5353
return JsonSerializer.SerializeToUtf8Bytes(data, stjSerializer.GetJsonSerializerOptions(formatting));
@@ -92,7 +92,7 @@ public static byte[] SerializeToBytes(
9292
SerializationFormatting formatting = SerializationFormatting.None
9393
)
9494
{
95-
if (serializer is SystemTextJsonSerializer stjSerializer)
95+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
9696
{
9797
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations.
9898
return JsonSerializer.SerializeToUtf8Bytes(data, type, stjSerializer.GetJsonSerializerOptions(formatting));
@@ -135,7 +135,7 @@ public static string SerializeToString<T>(
135135
SerializationFormatting formatting = SerializationFormatting.None
136136
)
137137
{
138-
if (serializer is SystemTextJsonSerializer stjSerializer)
138+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
139139
{
140140
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
141141
// serialize straight into string.
@@ -183,7 +183,7 @@ public static string SerializeToString(
183183
SerializationFormatting formatting = SerializationFormatting.None
184184
)
185185
{
186-
if (serializer is SystemTextJsonSerializer stjSerializer)
186+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
187187
{
188188
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
189189
// serialize straight into string.
@@ -234,7 +234,7 @@ public static void Serialize<T>(
234234
MemoryStreamFactory? memoryStreamFactory,
235235
SerializationFormatting formatting = SerializationFormatting.None)
236236
{
237-
if (serializer is SystemTextJsonSerializer stjSerializer)
237+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
238238
{
239239
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
240240
// serialize straight into the writer.
@@ -294,7 +294,7 @@ public static void Serialize(
294294
MemoryStreamFactory? memoryStreamFactory,
295295
SerializationFormatting formatting = SerializationFormatting.None)
296296
{
297-
if (serializer is SystemTextJsonSerializer stjSerializer)
297+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
298298
{
299299
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
300300
// serialize straight into the writer.
@@ -332,7 +332,7 @@ public static void Serialize(
332332
string input,
333333
MemoryStreamFactory? memoryStreamFactory = null)
334334
{
335-
if (serializer is SystemTextJsonSerializer stjSerializer)
335+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
336336
{
337337
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
338338
// deserialize straight from the span.
@@ -362,7 +362,7 @@ public static void Serialize(
362362
Type type,
363363
MemoryStreamFactory? memoryStreamFactory = null)
364364
{
365-
if (serializer is SystemTextJsonSerializer stjSerializer)
365+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
366366
{
367367
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
368368
// deserialize straight from the span.
@@ -391,7 +391,7 @@ public static void Serialize(
391391
ReadOnlySpan<byte> span,
392392
MemoryStreamFactory? memoryStreamFactory = null)
393393
{
394-
if (serializer is SystemTextJsonSerializer stjSerializer)
394+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
395395
{
396396
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
397397
// deserialize straight from the span.
@@ -421,7 +421,7 @@ public static void Serialize(
421421
Type type,
422422
MemoryStreamFactory? memoryStreamFactory = null)
423423
{
424-
if (serializer is SystemTextJsonSerializer stjSerializer)
424+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
425425
{
426426
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
427427
// deserialize straight from the span.
@@ -450,7 +450,7 @@ public static void Serialize(
450450
ReadOnlySpan<char> span,
451451
MemoryStreamFactory? memoryStreamFactory = null)
452452
{
453-
if (serializer is SystemTextJsonSerializer stjSerializer)
453+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
454454
{
455455
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
456456
// deserialize straight from the span.
@@ -480,7 +480,7 @@ public static void Serialize(
480480
Type type,
481481
MemoryStreamFactory? memoryStreamFactory = null)
482482
{
483-
if (serializer is SystemTextJsonSerializer stjSerializer)
483+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
484484
{
485485
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
486486
// deserialize straight from the span.
@@ -509,7 +509,7 @@ public static void Serialize(
509509
ref Utf8JsonReader reader,
510510
MemoryStreamFactory? memoryStreamFactory = null)
511511
{
512-
if (serializer is SystemTextJsonSerializer stjSerializer)
512+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
513513
{
514514
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
515515
// deserialize straight from the reader.
@@ -546,7 +546,7 @@ public static void Serialize(
546546
Type type,
547547
MemoryStreamFactory? memoryStreamFactory = null)
548548
{
549-
if (serializer is SystemTextJsonSerializer stjSerializer)
549+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
550550
{
551551
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
552552
// deserialize straight from the reader.
@@ -582,7 +582,7 @@ public static void Serialize(
582582
JsonNode node,
583583
MemoryStreamFactory? memoryStreamFactory = null)
584584
{
585-
if (serializer is SystemTextJsonSerializer stjSerializer)
585+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
586586
{
587587
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
588588
// deserialize straight from the node.
@@ -617,7 +617,7 @@ public static void Serialize(
617617
Type type,
618618
MemoryStreamFactory? memoryStreamFactory = null)
619619
{
620-
if (serializer is SystemTextJsonSerializer stjSerializer)
620+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
621621
{
622622
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
623623
// deserialize straight from the node.
@@ -651,7 +651,7 @@ public static void Serialize(
651651
JsonElement node,
652652
MemoryStreamFactory? memoryStreamFactory = null)
653653
{
654-
if (serializer is SystemTextJsonSerializer stjSerializer)
654+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(typeof(T)))
655655
{
656656
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
657657
// deserialize straight from the node.
@@ -686,7 +686,7 @@ public static void Serialize(
686686
Type type,
687687
MemoryStreamFactory? memoryStreamFactory = null)
688688
{
689-
if (serializer is SystemTextJsonSerializer stjSerializer)
689+
if (serializer is SystemTextJsonSerializer stjSerializer && stjSerializer.SupportsFastPath(type))
690690
{
691691
// When the serializer derives from `SystemTextJsonSerializer` we can avoid unnecessary allocations and
692692
// deserialize straight from the node.

src/Elastic.Transport/Requests/IUrlParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ namespace Elastic.Transport;
77
/// <summary> Implementers define an object that can be serialized as a query string parameter </summary>
88
public interface IUrlParameter
99
{
10-
/// <summary> Get the a string representation using <paramref name="settings"/> </summary>
11-
string GetString(ITransportConfiguration settings);
10+
/// <summary> Get the string representation using <paramref name="settings"/> </summary>
11+
public string GetString(ITransportConfiguration settings);
1212
}

0 commit comments

Comments
 (0)