Skip to content

Commit 9fcd857

Browse files
committed
Introduce providing json options as its own construct
1 parent 2871d17 commit 9fcd857

File tree

3 files changed

+74
-113
lines changed

3 files changed

+74
-113
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Text.Json;
8+
using System.Text.Json.Serialization;
9+
10+
namespace Elastic.Transport;
11+
12+
/// <summary>
13+
/// Provides an instance of <see cref="JsonSerializerOptions"/> to <see cref="SystemTextJsonSerializer"/>
14+
/// </summary>
15+
public interface IJsonSerializerOptionsProvider
16+
{
17+
/// <inheritdoc cref="IJsonSerializerOptionsProvider"/>
18+
JsonSerializerOptions CreateJsonSerializerOptions();
19+
}
20+
/// <summary>
21+
/// Default implementation of <see cref="IJsonSerializerOptionsProvider"/> specialized in providing more converters and
22+
/// altering the shared <see cref="JsonSerializerOptions"/> used by <see cref="SystemTextJsonSerializer"/> and its derrived classes
23+
/// </summary>
24+
public class TransportSerializerOptionsProvider : IJsonSerializerOptionsProvider
25+
{
26+
private readonly JsonSerializerOptions _options = new();
27+
28+
/// <inheritdoc cref="IJsonSerializerOptionsProvider"/>
29+
public JsonSerializerOptions? CreateJsonSerializerOptions() => _options;
30+
31+
/// <inheritdoc cref="TransportSerializerOptionsProvider"/>
32+
public TransportSerializerOptionsProvider() { }
33+
34+
/// <inheritdoc cref="TransportSerializerOptionsProvider"/>
35+
public TransportSerializerOptionsProvider(IReadOnlyCollection<JsonConverter> bakedIn, IReadOnlyCollection<JsonConverter>? userProvided, Action<JsonSerializerOptions>? optionsAction = null)
36+
{
37+
foreach (var converter in bakedIn)
38+
_options.Converters.Add(converter);
39+
40+
foreach (var converter in userProvided ?? [])
41+
_options.Converters.Add(converter);
42+
43+
optionsAction?.Invoke(_options);
44+
45+
}
46+
}
47+

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

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,35 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.Collections.Generic;
6-
using System.Collections.ObjectModel;
7-
using System.Linq;
86
using System.Text.Json;
97
using System.Text.Json.Serialization;
108

11-
using Elastic.Transport.Extensions;
12-
139
namespace Elastic.Transport;
1410

1511
/// <summary>
1612
/// Default low level request/response-serializer implementation for <see cref="Serializer"/> which serializes using
1713
/// the Microsoft <c>System.Text.Json</c> library
1814
/// </summary>
19-
internal sealed class LowLevelRequestResponseSerializer :
20-
SystemTextJsonSerializer
15+
internal sealed class LowLevelRequestResponseSerializer : SystemTextJsonSerializer
2116
{
2217
/// <summary>
2318
/// Provides a static reusable reference to an instance of <see cref="LowLevelRequestResponseSerializer"/> to promote reuse.
2419
/// </summary>
2520
internal static readonly LowLevelRequestResponseSerializer Instance = new();
2621

27-
private IReadOnlyCollection<JsonConverter> AdditionalConverters { get; }
28-
29-
private IList<JsonConverter> BakedInConverters { get; } = new List<JsonConverter>
30-
{
31-
new ExceptionConverter(),
32-
new ErrorCauseConverter(),
33-
new ErrorConverter(),
34-
new DynamicDictionaryConverter()
35-
};
36-
3722
/// <inheritdoc cref="LowLevelRequestResponseSerializer"/>>
3823
public LowLevelRequestResponseSerializer() : this(null) { }
3924

4025
/// <summary>
4126
/// <inheritdoc cref="LowLevelRequestResponseSerializer"/>>
4227
/// </summary>
4328
/// <param name="converters">Add more default converters onto <see cref="JsonSerializerOptions"/> being used</param>
44-
public LowLevelRequestResponseSerializer(IEnumerable<JsonConverter>? converters) =>
45-
AdditionalConverters = converters != null
46-
? new ReadOnlyCollection<JsonConverter>(converters.ToList())
47-
: EmptyReadOnly<JsonConverter>.Collection;
48-
49-
/// <summary>
50-
/// Creates <see cref="JsonSerializerOptions"/> used for serialization.
51-
/// Override on a derived serializer to change serialization.
52-
/// </summary>
53-
protected override JsonSerializerOptions? CreateJsonSerializerOptions()
54-
{
55-
var options = new JsonSerializerOptions
56-
{
57-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
58-
};
59-
60-
foreach (var converter in BakedInConverters)
61-
options.Converters.Add(converter);
62-
63-
foreach (var converter in AdditionalConverters)
64-
options.Converters.Add(converter);
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; })) { }
6536

66-
return options;
67-
}
6837
}

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

Lines changed: 19 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
// See the LICENSE file in the project root for more information
44

55
using System;
6+
using System.Collections.Generic;
67
using System.IO;
78
using System.Text.Json;
9+
using System.Text.Json.Serialization;
810
using System.Threading;
911
using System.Threading.Tasks;
1012

@@ -14,14 +16,23 @@ namespace Elastic.Transport;
1416
/// An abstract implementation of a transport <see cref="Serializer"/> which serializes using the Microsoft
1517
/// <c>System.Text.Json</c> library.
1618
/// </summary>
17-
public abstract class SystemTextJsonSerializer :
18-
Serializer
19+
public abstract class SystemTextJsonSerializer : Serializer
1920
{
20-
private readonly SemaphoreSlim _semaphore = new(1);
21+
private readonly JsonSerializerOptions? _options;
22+
private readonly JsonSerializerOptions? _indentedOptions;
2123

22-
private bool _initialized;
23-
private JsonSerializerOptions? _options;
24-
private JsonSerializerOptions? _indentedOptions;
24+
/// <summary>
25+
/// An abstract implementation of a transport <see cref="Serializer"/> which serializes using the Microsoft
26+
/// <c>System.Text.Json</c> library.
27+
/// </summary>
28+
protected SystemTextJsonSerializer(IJsonSerializerOptionsProvider? provider = null)
29+
{
30+
31+
provider ??= new TransportSerializerOptionsProvider();
32+
_options = provider.CreateJsonSerializerOptions();
33+
_indentedOptions = provider.CreateJsonSerializerOptions();
34+
_indentedOptions.WriteIndented = true;
35+
}
2536

2637
#region Serializer
2738

@@ -74,80 +85,14 @@ public override Task SerializeAsync<T>(T data, Stream stream,
7485

7586
#endregion Serializer
7687

77-
/// <summary>
78-
/// A factory method that can create an instance of <see cref="JsonSerializerOptions"/> that will
79-
/// be used when serializing.
80-
/// </summary>
81-
/// <returns></returns>
82-
protected abstract JsonSerializerOptions? CreateJsonSerializerOptions();
83-
84-
/// <summary>
85-
/// A callback function that is invoked after the <see cref="JsonSerializerOptions"/> have been created and the
86-
/// serializer got fully initialized.
87-
/// </summary>
88-
protected virtual void Initialized()
89-
{
90-
}
91-
9288
/// <summary>
9389
/// Returns the <see cref="JsonSerializerOptions"/> for this serializer, based on the given <paramref name="formatting"/>.
9490
/// </summary>
9591
/// <param name="formatting">The serialization formatting.</param>
9692
/// <returns>The requested <see cref="JsonSerializerOptions"/> or <c>null</c>, if the serializer is not initialized yet.</returns>
97-
protected internal JsonSerializerOptions? GetJsonSerializerOptions(SerializationFormatting formatting = SerializationFormatting.None)
98-
{
99-
Initialize();
100-
101-
return (formatting is SerializationFormatting.None)
102-
? _options
103-
: _indentedOptions;
104-
}
93+
protected internal JsonSerializerOptions? GetJsonSerializerOptions(SerializationFormatting formatting = SerializationFormatting.None) =>
94+
formatting is SerializationFormatting.None ? _options : _indentedOptions;
10595

106-
/// <summary>
107-
/// Initializes a serializer instance such that its <see cref="JsonSerializerOptions"/> are populated.
108-
/// </summary>
109-
private void Initialize()
110-
{
111-
// Exit early, if already initialized
112-
if (_initialized)
113-
return;
114-
115-
_semaphore.Wait();
116-
117-
try
118-
{
119-
// Exit early, if the current thread lost the race
120-
if (_initialized)
121-
return;
122-
123-
var options = CreateJsonSerializerOptions();
124-
125-
if (options is null)
126-
{
127-
_options = new JsonSerializerOptions();
128-
_indentedOptions = new JsonSerializerOptions
129-
{
130-
WriteIndented = true
131-
};
132-
}
133-
else
134-
{
135-
_options = options;
136-
_indentedOptions = new JsonSerializerOptions(options)
137-
{
138-
WriteIndented = true
139-
};
140-
}
141-
142-
_initialized = true;
143-
144-
Initialized();
145-
}
146-
finally
147-
{
148-
_semaphore.Release();
149-
}
150-
}
15196

15297
private static bool TryReturnDefault<T>(Stream? stream, out T deserialize)
15398
{

0 commit comments

Comments
 (0)