Skip to content

Commit 0a7d076

Browse files
committed
Merge branch 'jonyadamit-serializer-factory-settings' into 2.x
2 parents bac97eb + 8a8ae27 commit 0a7d076

File tree

9 files changed

+100
-59
lines changed

9 files changed

+100
-59
lines changed

docs/aggregations/writing-aggregations.asciidoc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ return s => s
144144
);
145145
----
146146
<1> a list of aggregation functions to apply
147-
148147
<2> Using LINQ's `Aggregate()` function to accumulate/apply all of the aggregation functions
149148

150149
Combining multipe `AggregationDescriptor`'s is also possible using the bitwise `&` operator
@@ -212,6 +211,5 @@ var maxPerChild = childAggregation.Max("max_per_child");
212211
maxPerChild.Should().NotBeNull(); <2>
213212
----
214213
<1> Do something with the average per child. Here we just assert it's not null
215-
216214
<2> Do something with the max per child. Here we just assert it's not null
217215

docs/client-concepts/connection-pooling/request-overrides/disable-sniff-ping-per-request.asciidoc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@ new ClientCall()
6464
);
6565
----
6666
<1> disable sniffing
67-
6867
<2> first call is a successful ping
69-
7068
<3> sniff on startup call happens here, on the second call
7169

7270
Now, let's disable pinging on the request
@@ -89,7 +87,6 @@ audit = await audit.TraceCall(
8987
);
9088
----
9189
<1> disable ping
92-
9390
<2> No ping after sniffing
9491

9592
Finally, let's demonstrate disabling both sniff and ping on the request
@@ -111,6 +108,5 @@ audit = await audit.TraceCall(
111108
);
112109
----
113110
<1> diable ping and sniff
114-
115111
<2> no ping or sniff before the call
116112

docs/client-concepts/high-level/inference/field-inference.asciidoc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,13 +500,9 @@ class Precedence
500500
}
501501
----
502502
<1> Even though this property has a NEST property mapping _and_ a `JsonProperty` attribute, We are going to provide a hard rename for it on ConnectionSettings later that should win.
503-
504503
<2> This property has both a NEST attribute and a `JsonProperty`, NEST should win.
505-
506504
<3> We should take the json property into account by itself
507-
508505
<4> This property we are going to special case in our custom serializer to resolve to ask
509-
510506
<5> We are going to register a DefaultFieldNameInferrer on ConnectionSettings that will uppercase all properties.
511507

512508
Here we create a custom serializer that renames any property named `AskSerializer` to `ask`

docs/client-concepts/high-level/inference/indices-paths.asciidoc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ ISearchRequest singleTypedRequest = new SearchDescriptor<Project>().Index(single
7474
var invalidSingleString = Nest.Indices.Index("name1, name2"); <3>
7575
----
7676
<1> specifying a single index using a string
77-
7877
<2> specifying a single index using a type
79-
8078
<3> an **invalid** single index name
8179

8280
==== Specifying multiple indices
@@ -100,9 +98,7 @@ ISearchRequest manyTypedRequest = new SearchDescriptor<Project>().Index(manyType
10098
((IUrlParameter)manyTypedRequest.Index).GetString(client.ConnectionSettings).Should().Be("project,devs"); <3>
10199
----
102100
<1> specifying multiple indices using strings
103-
104101
<2> specifying multiple indices using types
105-
106102
<3> The index names here come from the Connection Settings passed to `TestClient`. See the documentation on <<index-name-inference, Index Name Inference>> for more details.
107103

108104
==== Specifying All Indices

docs/client-concepts/low-level/connecting.asciidoc

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -314,24 +314,47 @@ therefore **we recommend doing some minimal introspection on the passed in certi
314314
If running on Core CLR, then a custom connection type must be created by deriving from `HttpConnection` and
315315
overriding the `CreateHttpClientHandler` method in order to set the `ServerCertificateCustomValidationCallback` property.
316316

317-
=== Overriding default Json.NET behavior
317+
=== Overriding Json.NET settings
318318

319319
Overriding the default Json.NET behaviour in NEST is an expert behavior but if you need to get to the nitty gritty, this can be really useful.
320-
First, create a subclass of the `JsonNetSerializer`
320+
321+
The easiest way is to create an instance of `SerializerFactory` that allows you to register a modification callback
322+
in the constructor
323+
324+
[source,csharp]
325+
----
326+
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
327+
328+
var connectionSettings = new ConnectionSettings(
329+
pool,
330+
new HttpConnection(),
331+
new SerializerFactory((jsonSettings, nestSettings) => jsonSettings.PreserveReferencesHandling = PreserveReferencesHandling.All));
332+
333+
var client = new ElasticClient(connectionSettings);
334+
----
335+
336+
A more involved and explicit way would be to implement your own JsonNetSerializer subclass.
337+
338+
NOTE: this is subject to change in the next major release. NEST relies heavily on stateful deserializers (that have access to the original
339+
request) for specialized features such a covariant search results. This requirement leaks into this abstraction.
321340

322341
[source,csharp]
323342
----
324343
public class MyJsonNetSerializer : JsonNetSerializer
325344
{
326-
public MyJsonNetSerializer(IConnectionSettingsValues settings) : base(settings) { }
345+
public MyJsonNetSerializer(IConnectionSettingsValues settings)
346+
: base(settings, (s, csv) => s.PreserveReferencesHandling = PreserveReferencesHandling.All) <1>
347+
{
348+
OverwriteDefaultSerializers((s, cvs) => ModifySerializerSettings(s)); <2>
349+
}
327350
328351
public int CallToModify { get; set; } = 0;
329352
330-
protected override void ModifyJsonSerializerSettings(JsonSerializerSettings settings) => ++CallToModify; <1>
353+
private void ModifySerializerSettings(JsonSerializerSettings settings) => ++CallToModify;
331354
332355
public int CallToContractConverter { get; set; } = 0;
333356
334-
protected override IList<Func<Type, JsonConverter>> ContractConverters => new List<Func<Type, JsonConverter>> <2>
357+
protected override IList<Func<Type, JsonConverter>> ContractConverters => new List<Func<Type, JsonConverter>> <3>
335358
{
336359
t => {
337360
CallToContractConverter++;
@@ -341,9 +364,11 @@ public class MyJsonNetSerializer : JsonNetSerializer
341364
342365
}
343366
----
344-
<1> Override ModifyJsonSerializerSettings if you need access to `JsonSerializerSettings`
367+
<1> Call this constructor if you only need access to `JsonSerializerSettings` without local state
368+
369+
<2> Call OverwriteDefaultSerializers if you need access to `JsonSerializerSettings` with local state
345370

346-
<2> You can inject contract resolved converters by implementing the ContractConverters property. This can be much faster then registering them on `JsonSerializerSettings.Converters`
371+
<3> You can inject contract resolved converters by implementing the ContractConverters property. This can be much faster then registering them on `JsonSerializerSettings.Converters`
347372

348373
You can then register a factory on `ConnectionSettings` to create an instance of your subclass instead.
349374
This is **_called once per instance_** of ConnectionSettings.

src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ namespace Nest
1414
public class ElasticContractResolver : DefaultContractResolver
1515
{
1616
private readonly IList<Func<Type, JsonConverter>> _contractConverters;
17-
public static JsonSerializer Empty { get; } = new JsonSerializer();
18-
17+
public static JsonSerializer Empty { get; } = new JsonSerializer();
18+
1919
/// <summary>
2020
/// ConnectionSettings can be requested by JsonConverter's.
2121
/// </summary>
22-
public IConnectionSettingsValues ConnectionSettings { get; private set; }
23-
22+
public IConnectionSettingsValues ConnectionSettings { get; private set; }
23+
2424
/// <summary>
2525
/// Signals to custom converter that it can get serialization state from one of the converters. Ugly but massive performance gain
2626
/// </summary>
@@ -36,22 +36,22 @@ protected override JsonContract CreateContract(Type objectType)
3636
{
3737
// cache contracts per connection settings
3838
return this.ConnectionSettings.Inferrer.Contracts.GetOrAdd(objectType, o =>
39-
{
40-
var contract = base.CreateContract(o);
41-
39+
{
40+
var contract = base.CreateContract(o);
41+
4242
if (typeof(IDictionary).IsAssignableFrom(o) && !typeof(IIsADictionary).IsAssignableFrom(o))
4343
contract.Converter = new VerbatimDictionaryKeysJsonConverter();
4444
if (typeof(IEnumerable<QueryContainer>).IsAssignableFrom(o))
4545
contract.Converter = new QueryContainerCollectionJsonConverter();
4646
else if (o == typeof(ServerError))
4747
contract.Converter = new ServerErrorJsonConverter();
48-
else if (o == typeof(DateTime) ||
49-
o == typeof(DateTime?) ||
50-
o == typeof(DateTimeOffset) ||
51-
o == typeof(DateTimeOffset?))
48+
else if (o == typeof(DateTime) ||
49+
o == typeof(DateTime?) ||
50+
o == typeof(DateTimeOffset) ||
51+
o == typeof(DateTimeOffset?))
5252
contract.Converter = new IsoDateTimeConverter();
53-
else if (o == typeof(TimeSpan) ||
54-
o == typeof(TimeSpan?))
53+
else if (o == typeof(TimeSpan) ||
54+
o == typeof(TimeSpan?))
5555
contract.Converter = new TimeSpanConverter();
5656

5757
if (this._contractConverters.HasAny())
@@ -102,12 +102,12 @@ private IEnumerable<Type> TypeWithInterfaces(Type objectType)
102102
}
103103

104104
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
105-
{
106-
// Only serialize explicitly implemented IProperty properties on attribute types
105+
{
106+
// Only serialize explicitly implemented IProperty properties on attribute types
107107
if (typeof(ElasticsearchPropertyAttributeBase).IsAssignableFrom(type))
108-
return PropertiesOfInterface<IProperty>(type, memberSerialization);
109-
110-
// Descriptors implement properties explicitly, these are not picked up by default
108+
return PropertiesOfInterface<IProperty>(type, memberSerialization);
109+
110+
// Descriptors implement properties explicitly, these are not picked up by default
111111
if (typeof(IDescriptor).IsAssignableFrom(type))
112112
return PropertiesOfAll(type, memberSerialization);
113113

@@ -155,9 +155,9 @@ protected override string ResolvePropertyName(string fieldName)
155155

156156
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
157157
{
158-
var property = base.CreateProperty(member, memberSerialization);
159-
160-
// Skip serialization of empty collections that has DefaultValueHandling set to Ignore.
158+
var property = base.CreateProperty(member, memberSerialization);
159+
160+
// Skip serialization of empty collections that has DefaultValueHandling set to Ignore.
161161
if (property.DefaultValueHandling.HasValue
162162
&& property.DefaultValueHandling.Value == DefaultValueHandling.Ignore
163163
&& !typeof(string).IsAssignableFrom(property.PropertyType)

src/Nest/CommonAbstractions/SerializationBehavior/JsonNetSerializer.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,25 @@ protected virtual void ModifyJsonSerializerSettings(JsonSerializerSettings setti
3030

3131
protected virtual IList<Func<Type, JsonConverter>> ContractConverters => null;
3232

33-
public JsonNetSerializer(IConnectionSettingsValues settings) : this(settings, null) { }
33+
public JsonNetSerializer(IConnectionSettingsValues settings, Action<JsonSerializerSettings, IConnectionSettingsValues> settingsModifier) : this(settings, null, settingsModifier) { }
34+
35+
public JsonNetSerializer(IConnectionSettingsValues settings) : this(settings, null, null) { }
3436

3537
/// <summary>
3638
/// this constructor is only here for stateful (de)serialization
3739
/// </summary>
3840
protected internal JsonNetSerializer(
3941
IConnectionSettingsValues settings,
40-
JsonConverter statefulConverter
42+
JsonConverter statefulConverter,
43+
Action<JsonSerializerSettings, IConnectionSettingsValues> settingsModifier = null
4144
)
4245
{
4346
this.Settings = settings;
44-
4547
var piggyBackState = statefulConverter == null ? null : new JsonConverterPiggyBackState { ActualJsonConverter = statefulConverter };
4648
// ReSharper disable once VirtualMemberCallInContructor
4749
this.ContractResolver = new ElasticContractResolver(this.Settings, this.ContractConverters) { PiggyBackState = piggyBackState };
4850

49-
this._defaultSerializer = JsonSerializer.Create(this.CreateSettings(SerializationFormatting.None));
50-
var indentedSerializer = JsonSerializer.Create(this.CreateSettings(SerializationFormatting.Indented));
51-
this._defaultSerializers = new Dictionary<SerializationFormatting, JsonSerializer>
52-
{
53-
{ SerializationFormatting.None, this._defaultSerializer },
54-
{ SerializationFormatting.Indented, indentedSerializer }
55-
};
51+
OverwriteDefaultSerializers(settingsModifier ?? ((s, csv) => { }));
5652
}
5753

5854
/// <summary>

src/Nest/CommonAbstractions/SerializationBehavior/SerializerFactory.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,24 @@ public interface ISerializerFactory
1818
public class SerializerFactory : ISerializerFactory
1919
{
2020
private Func<IConnectionSettingsValues, IElasticsearchSerializer> _serializerFactoryFunc;
21+
private Action<JsonSerializerSettings, IConnectionSettingsValues> _settingsModifier;
2122

2223
public SerializerFactory()
2324
{
2425

2526
}
26-
public SerializerFactory(Func<IConnectionSettingsValues , IElasticsearchSerializer> serializerFactoryFunc)
27+
public SerializerFactory(Func<IConnectionSettingsValues, IElasticsearchSerializer> serializerFactoryFunc) : this(serializerFactoryFunc, null) { }
28+
29+
public SerializerFactory(Action<JsonSerializerSettings, IConnectionSettingsValues> settingsModifier) : this(null, settingsModifier) { }
30+
31+
public SerializerFactory(Func<IConnectionSettingsValues, IElasticsearchSerializer> serializerFactoryFunc, Action<JsonSerializerSettings, IConnectionSettingsValues> settingsModifier)
2732
{
2833
this._serializerFactoryFunc = serializerFactoryFunc;
34+
this._settingsModifier = settingsModifier;
2935
}
3036

3137
public IElasticsearchSerializer Create(IConnectionSettingsValues settings) =>
32-
this._serializerFactoryFunc?.Invoke(settings) ?? new JsonNetSerializer(settings);
38+
this._serializerFactoryFunc?.Invoke(settings) ?? new JsonNetSerializer(settings, this._settingsModifier);
3339

3440
public IElasticsearchSerializer CreateStateful(IConnectionSettingsValues settings, JsonConverter converter) =>
3541
new JsonNetSerializer(settings, converter);

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

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,22 +289,50 @@ protected override HttpClientHandler CreateHttpClientHandler(RequestData request
289289
}
290290
#endif
291291

292-
/**=== Overriding default Json.NET behavior
292+
/**=== Overriding Json.NET settings
293293
*
294294
* Overriding the default Json.NET behaviour in NEST is an expert behavior but if you need to get to the nitty gritty, this can be really useful.
295-
* First, create a subclass of the `JsonNetSerializer`
296295
*/
296+
297+
/**
298+
* The easiest way is to create an instance of `SerializerFactory` that allows you to register a modification callback
299+
* in the constructor
300+
*/
301+
public void EasyWay()
302+
{
303+
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
304+
var connectionSettings = new ConnectionSettings(
305+
pool,
306+
new HttpConnection(),
307+
new SerializerFactory((jsonSettings, nestSettings) => jsonSettings.PreserveReferencesHandling = PreserveReferencesHandling.All));
308+
309+
var client = new ElasticClient(connectionSettings);
310+
}
311+
312+
313+
/**
314+
* A more involved and explicit way would be to implement your own JsonNetSerializer subclass.
315+
*
316+
* NOTE: this is subject to change in the next major release. NEST relies heavily on stateful deserializers (that have access to the original
317+
* request) for specialized features such a covariant search results. This requirement leaks into this abstraction.
318+
*
319+
*
320+
*/
297321
public class MyJsonNetSerializer : JsonNetSerializer
298322
{
299-
public MyJsonNetSerializer(IConnectionSettingsValues settings) : base(settings) { }
323+
public MyJsonNetSerializer(IConnectionSettingsValues settings)
324+
: base(settings, (s, csv) => s.PreserveReferencesHandling = PreserveReferencesHandling.All) //<1> Call this constructor if you only need access to `JsonSerializerSettings` without local state
325+
{
326+
OverwriteDefaultSerializers((s, cvs) => ModifySerializerSettings(s)); //<2> Call OverwriteDefaultSerializers if you need access to `JsonSerializerSettings` with local state
327+
}
300328

301329
public int CallToModify { get; set; } = 0;
302330

303-
protected override void ModifyJsonSerializerSettings(JsonSerializerSettings settings) => ++CallToModify; //<1> Override ModifyJsonSerializerSettings if you need access to `JsonSerializerSettings`
331+
private void ModifySerializerSettings(JsonSerializerSettings settings) => ++CallToModify;
304332

305333
public int CallToContractConverter { get; set; } = 0;
306334

307-
protected override IList<Func<Type, JsonConverter>> ContractConverters => new List<Func<Type, JsonConverter>> //<2> You can inject contract resolved converters by implementing the ContractConverters property. This can be much faster then registering them on `JsonSerializerSettings.Converters`
335+
protected override IList<Func<Type, JsonConverter>> ContractConverters => new List<Func<Type, JsonConverter>> //<3> You can inject contract resolved converters by implementing the ContractConverters property. This can be much faster then registering them on `JsonSerializerSettings.Converters`
308336
{
309337
t => {
310338
CallToContractConverter++;

0 commit comments

Comments
 (0)