|
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