|
2 | 2 | // Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
|
3 | 3 | // See the LICENSE file in the project root for more information
|
4 | 4 |
|
5 |
| -using System; |
6 | 5 | using System.Collections.Generic;
|
7 |
| -using System.Collections.ObjectModel; |
8 |
| -using System.IO; |
9 |
| -using System.Linq; |
10 | 6 | using System.Text.Json;
|
11 | 7 | using System.Text.Json.Serialization;
|
12 |
| -using System.Threading; |
13 |
| -using System.Threading.Tasks; |
14 |
| -using Elastic.Transport.Extensions; |
15 |
| -using static Elastic.Transport.SerializationFormatting; |
16 | 8 |
|
17 | 9 | namespace Elastic.Transport;
|
18 | 10 |
|
19 | 11 | /// <summary>
|
20 |
| -/// Default implementation for <see cref="Serializer"/>. This uses <see cref="JsonSerializer"/> from <code>System.Text.Json</code>. |
| 12 | +/// Default low level request/response-serializer implementation for <see cref="Serializer"/> which serializes using |
| 13 | +/// the Microsoft <c>System.Text.Json</c> library |
21 | 14 | /// </summary>
|
22 |
| -internal sealed class LowLevelRequestResponseSerializer : Serializer |
| 15 | +internal sealed class LowLevelRequestResponseSerializer : SystemTextJsonSerializer |
23 | 16 | {
|
24 | 17 | /// <summary>
|
25 | 18 | /// Provides a static reusable reference to an instance of <see cref="LowLevelRequestResponseSerializer"/> to promote reuse.
|
26 | 19 | /// </summary>
|
27 | 20 | internal static readonly LowLevelRequestResponseSerializer Instance = new();
|
28 | 21 |
|
29 |
| - private readonly Lazy<JsonSerializerOptions> _indented; |
30 |
| - private readonly Lazy<JsonSerializerOptions> _none; |
31 |
| - |
32 |
| - private IReadOnlyCollection<JsonConverter> AdditionalConverters { get; } |
33 |
| - |
34 |
| - private IList<JsonConverter> BakedInConverters { get; } = new List<JsonConverter> |
35 |
| - { |
36 |
| - new ExceptionConverter(), |
37 |
| - new ErrorCauseConverter(), |
38 |
| - new ErrorConverter(), |
39 |
| - new DynamicDictionaryConverter() |
40 |
| - }; |
41 |
| - |
42 | 22 | /// <inheritdoc cref="LowLevelRequestResponseSerializer"/>>
|
43 | 23 | public LowLevelRequestResponseSerializer() : this(null) { }
|
44 | 24 |
|
45 | 25 | /// <summary>
|
46 | 26 | /// <inheritdoc cref="LowLevelRequestResponseSerializer"/>>
|
47 | 27 | /// </summary>
|
48 | 28 | /// <param name="converters">Add more default converters onto <see cref="JsonSerializerOptions"/> being used</param>
|
49 |
| - public LowLevelRequestResponseSerializer(IEnumerable<JsonConverter>? converters) |
50 |
| - { |
51 |
| - AdditionalConverters = converters != null |
52 |
| - ? new ReadOnlyCollection<JsonConverter>(converters.ToList()) |
53 |
| - : EmptyReadOnly<JsonConverter>.Collection; |
54 |
| - _indented = new Lazy<JsonSerializerOptions>(() => CreateSerializerOptions(Indented)); |
55 |
| - _none = new Lazy<JsonSerializerOptions>(() => CreateSerializerOptions(None)); |
56 |
| - } |
57 |
| - |
58 |
| - /// <summary> |
59 |
| - /// Creates <see cref="JsonSerializerOptions"/> used for serialization. |
60 |
| - /// Override on a derived serializer to change serialization. |
61 |
| - /// </summary> |
62 |
| - public JsonSerializerOptions CreateSerializerOptions(SerializationFormatting formatting) |
63 |
| - { |
64 |
| - var options = new JsonSerializerOptions |
65 |
| - { |
66 |
| - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, |
67 |
| - WriteIndented = formatting == Indented, |
68 |
| - }; |
69 |
| - foreach (var converter in BakedInConverters) |
70 |
| - options.Converters.Add(converter); |
71 |
| - foreach (var converter in AdditionalConverters) |
72 |
| - options.Converters.Add(converter); |
73 |
| - |
74 |
| - return options; |
75 |
| - |
76 |
| - } |
77 |
| - |
78 |
| - private static bool TryReturnDefault<T>(Stream? stream, out T deserialize) |
79 |
| - { |
80 |
| - deserialize = default; |
81 |
| - return stream == null || stream == Stream.Null || (stream.CanSeek && stream.Length == 0); |
82 |
| - } |
83 |
| - |
84 |
| - private JsonSerializerOptions GetFormatting(SerializationFormatting formatting) => formatting == None ? _none.Value : _indented.Value; |
85 |
| - |
86 |
| - /// <inheritdoc cref="Serializer.Deserialize"/>> |
87 |
| - public override object Deserialize(Type type, Stream stream) |
88 |
| - { |
89 |
| - if (TryReturnDefault(stream, out object deserialize)) return deserialize; |
90 |
| - |
91 |
| - return JsonSerializer.Deserialize(stream, type, _none.Value)!; |
92 |
| - } |
93 |
| - |
94 |
| - /// <inheritdoc cref="Serializer.Deserialize{T}"/>> |
95 |
| - public override T Deserialize<T>(Stream stream) |
96 |
| - { |
97 |
| - if (TryReturnDefault(stream, out T deserialize)) return deserialize; |
98 |
| - |
99 |
| - return JsonSerializer.Deserialize<T>(stream, _none.Value); |
100 |
| - } |
101 |
| - |
102 |
| - /// <inheritdoc cref="Serializer.Serialize{T}"/>> |
103 |
| - public override void Serialize<T>(T data, Stream stream, SerializationFormatting formatting = None) |
104 |
| - { |
105 |
| - using var writer = new Utf8JsonWriter(stream); |
106 |
| - if (data == null) |
107 |
| - JsonSerializer.Serialize(writer, null, typeof(object), GetFormatting(formatting)); |
108 |
| - //TODO validate if we can avoid boxing by checking if data is typeof(object) |
109 |
| - else |
110 |
| - JsonSerializer.Serialize(writer, data, data.GetType(), GetFormatting(formatting)); |
111 |
| - } |
112 |
| - |
113 |
| - /// <inheritdoc cref="Serializer.SerializeAsync{T}"/>> |
114 |
| - public override async Task SerializeAsync<T>(T data, Stream stream, SerializationFormatting formatting = None, |
115 |
| - CancellationToken cancellationToken = default |
116 |
| - ) |
117 |
| - { |
118 |
| - if (data == null) |
119 |
| - await JsonSerializer.SerializeAsync(stream, null, typeof(object), GetFormatting(formatting), cancellationToken).ConfigureAwait(false); |
120 |
| - else |
121 |
| - await JsonSerializer.SerializeAsync(stream, data, data.GetType(), GetFormatting(formatting), cancellationToken).ConfigureAwait(false); |
122 |
| - } |
123 |
| - |
124 |
| - /// <inheritdoc cref="Serializer.DeserializeAsync"/>> |
125 |
| - public override ValueTask<object> DeserializeAsync(Type type, Stream stream, CancellationToken cancellationToken = default) |
126 |
| - { |
127 |
| - if (TryReturnDefault(stream, out object deserialize)) return new ValueTask<object>(deserialize); |
128 |
| - |
129 |
| - return JsonSerializer.DeserializeAsync(stream, type, _none.Value, cancellationToken); |
130 |
| - } |
131 |
| - |
132 |
| - /// <inheritdoc cref="Serializer.DeserializeAsync{T}"/>> |
133 |
| - public override ValueTask<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default) |
134 |
| - { |
135 |
| - if (TryReturnDefault(stream, out T deserialize)) return new ValueTask<T>(deserialize); |
| 29 | + public LowLevelRequestResponseSerializer(IReadOnlyCollection<JsonConverter>? converters) |
| 30 | + : base(new TransportSerializerOptionsProvider([ |
| 31 | + new ExceptionConverter(), |
| 32 | + new ErrorCauseConverter(), |
| 33 | + new ErrorConverter(), |
| 34 | + new DynamicDictionaryConverter() |
| 35 | + ], converters, options => { options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; })) { } |
136 | 36 |
|
137 |
| - return JsonSerializer.DeserializeAsync<T>(stream, _none.Value, cancellationToken); |
138 |
| - } |
139 | 37 | }
|
0 commit comments