Skip to content

Commit b42b6f0

Browse files
tsliangTai Sassen-Liang
authored andcommitted
Lazily resolve JsonSerializers in JsonNetSerializer
The current JsonNetSerializer implementation creates the default serializers within its constructor, which then calls ModifyJsonSerializerSettings. This means that a subclass of JsonNetSerializer cannot modify the JsonSerializerSettings with fields set by the constructor, because the order of operations is: 1. Call subclass constructor with parameter A, which will set subclass field A; field A will be used in ModifyJsonSerializerSettings. 2. Subclass constructor calls JsonNetSerializer constructor with :base() 3. JsonNetSerializer constructor calls ModifyJsonSerializerSettings as part of JsonSerializer.Create 4. ModifyJsonSerializerSettings makes use of default value for field A, because it has not been set yet. 5. JsonSerializer constructor exits 6. Subclass constructor sets value of field A from parameter A. By using the Lazy<T> class, we can ensure that ModifyJsonSerializerSettings is called after the subclass constructor has exited, which allows fields to be set and thereby used in ModifyJsonSerializerSettings. The unit test in Connection.doc.cs has been expanded to demonstrate this capability with a (fairly trivial) use case.
1 parent a369b5d commit b42b6f0

File tree

2 files changed

+33
-18
lines changed

2 files changed

+33
-18
lines changed

src/Nest/CommonAbstractions/SerializationBehavior/JsonNetSerializer.cs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,40 +19,41 @@ public class JsonNetSerializer : IElasticsearchSerializer
1919
protected ElasticContractResolver ContractResolver { get; }
2020

2121
//todo this internal smells
22-
internal JsonSerializer Serializer => _defaultSerializer;
22+
internal JsonSerializer Serializer => _defaultSerializer.Value;
2323

24-
private readonly Dictionary<SerializationFormatting, JsonSerializer> _defaultSerializers;
25-
private readonly JsonSerializer _defaultSerializer;
24+
private readonly Lazy<Dictionary<SerializationFormatting, JsonSerializer>> _defaultSerializers;
25+
private readonly Lazy<JsonSerializer> _defaultSerializer;
2626

2727
protected virtual void ModifyJsonSerializerSettings(JsonSerializerSettings settings) { }
2828
protected virtual IList<Func<Type, JsonConverter>> ContractConverters => null;
2929

3030
public JsonNetSerializer(IConnectionSettingsValues settings) : this(settings, null) { }
3131

3232
/// <summary>
33-
/// this constructor is only here for stateful (de)serialization
33+
/// this constructor is only here for stateful (de) serialization
3434
/// </summary>
3535
internal JsonNetSerializer(IConnectionSettingsValues settings, JsonConverter stateFullConverter)
3636
{
3737
this.Settings = settings;
3838
var piggyBackState = stateFullConverter == null ? null : new JsonConverterPiggyBackState { ActualJsonConverter = stateFullConverter };
39+
3940
// ReSharper disable once VirtualMemberCallInContructor
4041
this.ContractResolver = new ElasticContractResolver(this.Settings, this.ContractConverters) { PiggyBackState = piggyBackState };
4142

42-
this._defaultSerializer = JsonSerializer.Create(this.CreateSettings(SerializationFormatting.None));
43-
//this._defaultSerializer.Formatting = Formatting.None;
44-
var indentedSerializer = JsonSerializer.Create(this.CreateSettings(SerializationFormatting.Indented));
45-
//indentedSerializer.Formatting = Formatting.Indented;
46-
this._defaultSerializers = new Dictionary<SerializationFormatting, JsonSerializer>
43+
this._defaultSerializer = new Lazy<JsonSerializer>(() => JsonSerializer.Create(this.CreateSettings(SerializationFormatting.None)));
44+
45+
var indentedSerializer = new Lazy<JsonSerializer>(() => JsonSerializer.Create(this.CreateSettings(SerializationFormatting.Indented)));
46+
47+
this._defaultSerializers = new Lazy<Dictionary<SerializationFormatting, JsonSerializer>>(() => new Dictionary<SerializationFormatting, JsonSerializer>()
4748
{
48-
{ SerializationFormatting.None, this._defaultSerializer },
49-
{ SerializationFormatting.Indented, indentedSerializer }
50-
};
49+
{ SerializationFormatting.None, this._defaultSerializer.Value },
50+
{ SerializationFormatting.Indented, indentedSerializer.Value }
51+
});
5152
}
5253

5354
public virtual void Serialize(object data, Stream writableStream, SerializationFormatting formatting = SerializationFormatting.Indented)
5455
{
55-
var serializer = _defaultSerializers[formatting];
56+
var serializer = _defaultSerializers.Value[formatting];
5657
using (var writer = new StreamWriter(writableStream, ExpectedEncoding, 8096, leaveOpen: true))
5758
using (var jsonWriter = new JsonTextWriter(writer))
5859
{
@@ -87,7 +88,7 @@ public virtual T Deserialize<T>(Stream stream)
8788
using (var streamReader = new StreamReader(stream))
8889
using (var jsonTextReader = new JsonTextReader(streamReader))
8990
{
90-
var t = this._defaultSerializer.Deserialize(jsonTextReader, typeof(T));
91+
var t = this.Serializer.Deserialize(jsonTextReader, typeof(T));
9192
return (T)t;
9293
}
9394
}
@@ -117,4 +118,4 @@ private JsonSerializerSettings CreateSettings(SerializationFormatting formatting
117118
return settings;
118119
}
119120
}
120-
}
121+
}

src/Tests/ClientConcepts/LowLevel/Connecting.doc.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,23 @@ public void ConfiguringSSL()
274274
*/
275275
public class MyJsonNetSerializer : JsonNetSerializer
276276
{
277-
public MyJsonNetSerializer(IConnectionSettingsValues settings) : base(settings) { }
277+
private int _maxDepth;
278+
279+
public MyJsonNetSerializer(IConnectionSettingsValues settings, int maxDepth)
280+
: base(settings)
281+
{
282+
this._maxDepth = maxDepth;
283+
}
278284

279285
public int CallToModify { get; set; } = 0;
280286

281-
protected override void ModifyJsonSerializerSettings(JsonSerializerSettings settings) => ++CallToModify; //<1> Override ModifyJsonSerializerSettings if you need access to `JsonSerializerSettings`
287+
public int SetMaxDepth { get; set; }
288+
289+
protected override void ModifyJsonSerializerSettings(JsonSerializerSettings settings)
290+
{
291+
++CallToModify;
292+
SetMaxDepth = _maxDepth;
293+
} //<1> Override ModifyJsonSerializerSettings if you need access to `JsonSerializerSettings`
282294

283295
public int CallToContractConverter { get; set; } = 0;
284296

@@ -300,7 +312,8 @@ public MyJsonNetSerializer(IConnectionSettingsValues settings) : base(settings)
300312
public void ModifyJsonSerializerSettingsIsCalled()
301313
{
302314
var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
303-
var settings = new ConnectionSettings(connectionPool, new InMemoryConnection(), s => new MyJsonNetSerializer(s));
315+
var maxDepth = 8;
316+
var settings = new ConnectionSettings(connectionPool, new InMemoryConnection(), s => new MyJsonNetSerializer(s, maxDepth));
304317
var client = new ElasticClient(settings);
305318
client.RootNodeInfo();
306319
client.RootNodeInfo();
@@ -309,6 +322,7 @@ public void ModifyJsonSerializerSettingsIsCalled()
309322

310323
serializer.SerializeToString(new Project { });
311324
serializer.CallToContractConverter.Should().BeGreaterThan(0);
325+
serializer.SetMaxDepth.Should().Be(maxDepth);
312326
}
313327
}
314328
}

0 commit comments

Comments
 (0)