Skip to content

Commit 9d9bccd

Browse files
committed
Adding "Configuring custom JsonSerializerOptions" section
1 parent 2350084 commit 9d9bccd

File tree

4 files changed

+83
-35
lines changed

4 files changed

+83
-35
lines changed

docs/client-concepts/client-concepts.asciidoc

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ Source serialization refers to the process of (de)serializing POCO types in cons
1919

2020
* <<modeling-documents-with-types,Modelling documents with types>>
2121

22-
* <<custom-serialization,Custom serialization>>
22+
* <<customizing-source-serialization,Customizing source serialization>>
2323

2424
include::serialization/modeling-documents-with-types.asciidoc[]
2525

2626
include::serialization/custom-serialization.asciidoc[]
27+
28+
NOTE: This is still a work in progress, more sections will be added in the future.

docs/client-concepts/serialization/custom-serialization.asciidoc

+55-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
[[custom-serialization]]
2-
==== Custom serialization
1+
[[customizing-source-serialization]]
2+
==== Customizing source serialization
33

44
The built-in source serializer handles most POCO document models correctly. Sometimes, you may need further control over how your types are serialized.
55

@@ -8,19 +8,19 @@ NOTE: The built-in source serializer uses the Microsoft https://learn.microsoft.
88
[[system-text-json-attributes]]
99
===== Using `System.Text.Json` attributes
1010

11-
`System.Text.Json` includes attributes that can be applied to types and properties to control how they are serialized. These can be applied to your POCO document types to perform actions such as controlling the name of a property, or ignoring a property entirely. Visit the https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview[Microsoft documentation for further examples].
11+
`System.Text.Json` includes attributes that can be applied to types and properties to control their serialization. These can be applied to your POCO document types to perform actions such as controlling the name of a property or ignoring a property entirely. Visit the https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview[Microsoft documentation for further examples].
1212

13-
We can model a document to represent data about a person using a regular class (POCO), applying `System.Text.Json`` attributes as necessary.
13+
We can model a document to represent data about a person using a regular class (POCO), applying `System.Text.Json` attributes as necessary.
1414

1515
[source,csharp]
1616
----
1717
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings-serialization]
1818
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=person-class-with-attributes]
1919
----
20-
<1> the `JsonPropertyName` attribute is used to provide a specific name (`forename`) for the `FirstName` property when serialized.
21-
<2> the `JsonIgnore` attribute is used to prevent the `Age` property from appearing in the serialized JSON.
20+
<1> The `JsonPropertyName` attribute is used to ensure the `FirstName` property uses the JSON property name `forename` when serialized.
21+
<2> The `JsonIgnore` attribute is used to prevent the `Age` property from appearing in the serialized JSON.
2222

23-
We can then index the an instance of the document into {es}.
23+
We can then index an instance of the document into {es}.
2424

2525
[source,csharp]
2626
----
@@ -45,11 +45,56 @@ TODO
4545
[[configuring-custom-jsonserializeroptions]]
4646
===== Configuring custom `JsonSerializerOptions`
4747

48-
TODO
48+
The default source serializer applies a set of standard `JsonSerializerOptions` when serializing source document types. In some circumstances, you may wish to override some of our defaults. This is possible by creating an instance of `DefaultSourceSerializer` and passing an `Action<JsonSerializerOptions>` which is applied after our defaults have been set. This mechanism allows you to apply additional settings, or change the value of our defaults.
49+
50+
The `DefaultSourceSerializer` includes a constructor that accepts the current `IElasticsearchClientSettings` and a `configureOptions` `Action`.
51+
52+
[source,csharp]
53+
----
54+
public DefaultSourceSerializer(IElasticsearchClientSettings settings, Action<JsonSerializerOptions> configureOptions);
55+
----
56+
57+
Our application defines the following `Person` class, which models a document we will index to {es}.
58+
59+
[source,csharp]
60+
----
61+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=person-class]
62+
----
63+
64+
We want to serialize our source document using Pascal Casing for the JSON properties. Since the options applied in the `DefaultSouceSerializer` set the `PropertyNamingPolicy` to `JsonNamingPolicy.CamelCase`, we must override this setting.
65+
66+
[source,csharp]
67+
----
68+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings]
69+
private async Task SerializeWithCustomOptionsAsync()
70+
{
71+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=custom-options-local-function]
72+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=create-client]
73+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=index-person]
74+
}
75+
----
76+
<1> A local function can be defined, accepting a `JsonSerializerOptions` parameter. Here, we set `PropertyNamingPolicy` to `null`. This returns to the default behavior for `System.Text.Json`, which uses Pascal Case.
77+
<2> When creating the `ElasticsearchClientSettings`, we supply a `SourceSerializerFactory` using a lambda. The factory function creates a new instance of `DefaultSourceSerializer`, passing in the `settings` and our `ConfigureOptions` local function. We have now configured the settings with a custom instance of the source serializer.
78+
79+
The `Person` instance is serialized, with the source serializer serializing the POCO property named `FirstName` using Pascal Case.
80+
81+
[source,javascript]
82+
----
83+
{
84+
"FirstName": "Steve"
85+
}
86+
----
87+
88+
As an alternative to using a local function, we could store an `Action<JsonSerializerOptions>` into a variable instead, which can be passed to the `DefaultSouceSerializer` constructor.
89+
90+
[source,csharp]
91+
----
92+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=custom-options-action]
93+
----
4994

5095
[[injecting-custom-serializer]]
5196
===== Injecting a custom serializer
5297

5398
TODO
54-
- Deriving from SystemTextJsonSerializer
55-
- Deriving from Serializer
99+
- Deriving from SystemTextJsonSerializer for enum Converter
100+
- Deriving from Serializer for different library

docs/client-concepts/serialization/modeling-documents-with-types.asciidoc

+1-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,4 @@ The index request is serialized, with the source serializer handling the `MyDocu
3434
{
3535
"stringProperty": "value"
3636
}
37-
----
38-
39-
NOTE: This is still a work in progress, more sections will be added in the future.
37+
----

tests/Tests/Documentation/ClientConcepts/Serialization/CustomSerializationTests.cs

+24-21
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,20 @@ public async Task CustomizingJsonSerializerOptions()
3535
{
3636
// This example resets the PropertyNamingPolicy, such that the existing C# Pascal case is sent in the JSON.
3737

38-
//tag::custom-options-local-function[]
39-
static void ConfigureOptions(JsonSerializerOptions o) => o.PropertyNamingPolicy = null; // <1>
40-
//end::custom-options-local-function[]
41-
42-
//tag::create-client[]
43-
var nodePool = new SingleNodePool(new Uri("http://localhost:9200"));
44-
var settings = new ElasticsearchClientSettings(
45-
nodePool,
46-
sourceSerializer: (defaultSerializer, settings) =>
47-
new DefaultSourceSerializer(settings, ConfigureOptions)); // <3>
48-
var client = new ElasticsearchClient(settings);
49-
//end::create-client[]
38+
#pragma warning disable format
39+
//tag::custom-options-local-function[]
40+
static void ConfigureOptions(JsonSerializerOptions o) => o.PropertyNamingPolicy = null; // <1>
41+
//end::custom-options-local-function[]
42+
43+
//tag::create-client[]
44+
var nodePool = new SingleNodePool(new Uri("http://localhost:9200"));
45+
var settings = new ElasticsearchClientSettings(
46+
nodePool,
47+
sourceSerializer: (defaultSerializer, settings) =>
48+
new DefaultSourceSerializer(settings, ConfigureOptions)); // <2>
49+
var client = new ElasticsearchClient(settings);
50+
//end::create-client[]
51+
#pragma warning restore format
5052

5153
// Needed for the test assertion as we should use the in memory connection and disable direct streaming.
5254
// We don't want to include those in the docs as it may mislead or confuse developers.
@@ -59,10 +61,12 @@ public async Task CustomizingJsonSerializerOptions()
5961
.DisableDirectStreaming();
6062
client = new ElasticsearchClient(settings);
6163

62-
//tag::index-person[]
63-
var person = new Person { FirstName = "Steve" };
64-
var indexResponse = await client.IndexAsync(person, "my-index-name");
65-
//end::index-person[]
64+
#pragma warning disable format
65+
//tag::index-person[]
66+
var person = new Person { FirstName = "Steve" };
67+
var indexResponse = await client.IndexAsync(person, "my-index-name");
68+
//end::index-person[]
69+
#pragma warning restore format
6670

6771
var requestJson = Encoding.UTF8.GetString(indexResponse.ApiCallDetails.RequestBodyInBytes);
6872
await Verifier.Verify(requestJson);
@@ -71,11 +75,10 @@ public async Task CustomizingJsonSerializerOptions()
7175
var deserializedPerson = client.SourceSerializer.Deserialize<Person>(ms);
7276
deserializedPerson.FirstName.Should().Be("Steve");
7377

74-
// Alternative example using an Action
75-
76-
//tag::custom-options-action[]
77-
Action<JsonSerializerOptions> configureOptions = o => o.PropertyNamingPolicy = null; // <3>
78-
//end::custom-options-action[]
78+
// Alternative example using an Action
79+
//tag::custom-options-action[]
80+
Action<JsonSerializerOptions> configureOptions = o => o.PropertyNamingPolicy = null; // <3>
81+
//end::custom-options-action[]
7982
}
8083

8184
#pragma warning disable format

0 commit comments

Comments
 (0)