Skip to content

Commit 214de0f

Browse files
authored
Fix docs build (#8540)
1 parent 6e5b19e commit 214de0f

File tree

83 files changed

+3727
-2928
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+3727
-2928
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[[client-concepts]]
2+
= Client concepts
3+
4+
The .NET client for {es} maps closely to the original {es} API. All
5+
requests and responses are exposed through types, making it ideal for getting up and running quickly.
6+
7+
[[serialization]]
8+
== Serialization
9+
10+
By default, the .NET client for {es} uses the Microsoft System.Text.Json library for serialization. The client understands how to serialize and
11+
deserialize the request and response types correctly. It also handles (de)serialization of user POCO types representing documents read or written to {es}.
12+
13+
The client has two distinct serialization responsibilities - serialization of the types owned by the `Elastic.Clients.Elasticsearch` library and serialization of source documents, modeled in application code. The first responsibility is entirely internal; the second is configurable.
14+
15+
[[source-serialization]]
16+
=== Source serialization
17+
18+
Source serialization refers to the process of (de)serializing POCO types in consumer applications as source documents indexed and retrieved from {es}. A source serializer implementation handles serialization, with the default implementation using the `System.Text.Json` library. As a result, you may use `System.Text.Json` attributes and converters to control the serialization behavior.
19+
20+
* <<modeling-documents-with-types,Modelling documents with types>>
21+
22+
* <<customizing-source-serialization,Customizing source serialization>>
23+
24+
include::serialization/modeling-documents-with-types.asciidoc[]
25+
26+
include::serialization/custom-serialization.asciidoc[]
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
[[customizing-source-serialization]]
2+
==== Customizing source serialization
3+
4+
The built-in source serializer handles most POCO document models correctly. Sometimes, you may need further control over how your types are serialized.
5+
6+
NOTE: The built-in source serializer uses the https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview[Microsoft `System.Text.Json` library] internally. You can apply `System.Text.Json` attributes and converters to control the serialization of your document types.
7+
8+
[discrete]
9+
[[system-text-json-attributes]]
10+
===== Using `System.Text.Json` attributes
11+
12+
`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].
13+
14+
We can model a document to represent data about a person using a regular class (POCO), applying `System.Text.Json` attributes as necessary.
15+
16+
[source,csharp]
17+
----
18+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings-serialization]
19+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=person-class-with-attributes]
20+
----
21+
<1> The `JsonPropertyName` attribute ensures the `FirstName` property uses the JSON name `forename` when serialized.
22+
<2> The `JsonIgnore` attribute prevents the `Age` property from appearing in the serialized JSON.
23+
24+
We can then index an instance of the document into {es}.
25+
26+
[source,csharp]
27+
----
28+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings]
29+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=index-person-with-attributes]
30+
----
31+
32+
The index request is serialized, with the source serializer handling the `Person` type, serializing the POCO property named `FirstName` to the JSON object member named `forename`. The `Age` property is ignored and does not appear in the JSON.
33+
34+
[source,javascript]
35+
----
36+
{
37+
"forename": "Steve"
38+
}
39+
----
40+
41+
[discrete]
42+
[[configuring-custom-jsonserializeroptions]]
43+
===== Configuring custom `JsonSerializerOptions`
44+
45+
The default source serializer applies a set of standard `JsonSerializerOptions` when serializing source document types. In some circumstances, you may need to override some of our defaults. This is achievable 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.
46+
47+
The `DefaultSourceSerializer` includes a constructor that accepts the current `IElasticsearchClientSettings` and a `configureOptions` `Action`.
48+
49+
[source,csharp]
50+
----
51+
public DefaultSourceSerializer(IElasticsearchClientSettings settings, Action<JsonSerializerOptions> configureOptions);
52+
----
53+
54+
Our application defines the following `Person` class, which models a document we will index to {es}.
55+
56+
[source,csharp]
57+
----
58+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=person-class]
59+
----
60+
61+
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. After configuring the `ElasticsearchClientSettings`, we index our document to {es}.
62+
63+
[source,csharp]
64+
----
65+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings]
66+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=custom-options-local-function]
67+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=create-client]
68+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=index-person]
69+
----
70+
<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.
71+
<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.
72+
73+
The `Person` instance is serialized, with the source serializer serializing the POCO property named `FirstName` using Pascal Case.
74+
75+
[source,javascript]
76+
----
77+
{
78+
"FirstName": "Steve"
79+
}
80+
----
81+
82+
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.
83+
84+
[source,csharp]
85+
----
86+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=custom-options-action]
87+
----
88+
89+
[discrete]
90+
[[registering-custom-converters]]
91+
===== Registering custom `System.Text.Json` converters
92+
93+
In certain more advanced situations, you may have types which require further customization during serialization than is possible using `System.Text.Json` property attributes. In these cases, the recommendation from Microsoft is to leverage a custom `JsonConverter`. Source document types serialized using the `DefaultSourceSerializer` can leverage the power of custom converters.
94+
95+
For this example, our application has a document class that should use a legacy JSON structure to continue operating with existing indexed documents. Several options are available, but we'll apply a custom converter in this case.
96+
97+
Our class is defined, and the `JsonConverter` attribute is applied to the class type, specifying the type of a custom converter.
98+
99+
[source,csharp]
100+
----
101+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings-serialization]
102+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=customer-with-jsonconverter-attribute]
103+
----
104+
<1> The `JsonConverter` attribute signals to `System.Text.Json` that it should use a converter of type `CustomerConverter` when serializing instances of this class.
105+
106+
When serializing this class, rather than include a string value representing the value of the `CustomerType` property, we must send a boolean property named `isStandard`. This requirement can be achieved with a custom JsonConverter implementation.
107+
108+
[source,csharp]
109+
----
110+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=converter-usings]
111+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=customer-converter]
112+
----
113+
<1> When reading, this converter reads the `isStandard` boolean and translate this to the correct `CustomerType` enum value.
114+
<2> When writing, this converter translates the `CustomerType` enum value to an `isStandard` boolean property.
115+
116+
We can then index a customer document into {es}.
117+
118+
[source,csharp]
119+
----
120+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings]
121+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=index-customer-with-converter]
122+
----
123+
124+
The `Customer` instance is serialized using the custom converter, creating the following JSON document.
125+
126+
[source,javascript]
127+
----
128+
{
129+
"customerName": "Customer Ltd",
130+
"isStandard": false
131+
}
132+
----
133+
134+
[discrete]
135+
[[creating-custom-system-text-json-serializer]]
136+
===== Creating a custom `SystemTextJsonSerializer`
137+
138+
The built-in `DefaultSourceSerializer` includes the registration of `JsonConverter` instances which apply during source serialization. In most cases, these provide the proper behavior for serializing source documents, including those which use `Elastic.Clients.Elasticsearch` types on their properties.
139+
140+
An example of a situation where you may require more control over the converter registration order is for serializing `enum` types. The `DefaultSourceSerializer` registers the `System.Text.Json.Serialization.JsonStringEnumConverter`, so enum values are serialized using their string representation. Generally, this is the preferred option for types used to index documents to {es}.
141+
142+
In some scenarios, you may need to control the string value sent for an enumeration value. That is not directly supported in `System.Text.Json` but can be achieved by creating a custom `JsonConverter` for the `enum` type you wish to customize. In this situation, it is not sufficient to use the `JsonConverterAttribute` on the `enum` type to register the converter. `System.Text.Json` will prefer the converters added to the `Converters` collection on the `JsonSerializerOptions` over an attribute applied to an `enum` type. It is, therefore, necessary to either remove the `JsonStringEnumConverter` from the `Converters` collection or register a specialized converter for your `enum` type before the `JsonStringEnumConverter`.
143+
144+
The latter is possible via several techniques. When using the {es} .NET library, we can achieve this by deriving from the abstract `SystemTextJsonSerializer` class.
145+
146+
Here we have a POCO which uses the `CustomerType` enum as the type for a property.
147+
148+
[source,csharp]
149+
----
150+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings-serialization]
151+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=customer-without-jsonconverter-attribute]
152+
----
153+
154+
To customize the strings used during serialization of the `CustomerType`, we define a custom `JsonConverter` specific to our `enum` type.
155+
156+
[source,csharp]
157+
----
158+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings-serialization]
159+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=customer-type-converter]
160+
----
161+
<1> When reading, this converter translates the string used in the JSON to the matching enum value.
162+
<2> When writing, this converter translates the `CustomerType` enum value to a custom string value written to the JSON.
163+
164+
We create a serializer derived from `SystemTextJsonSerializer` to give us complete control of converter registration order.
165+
166+
[source,csharp]
167+
----
168+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=derived-converter-usings]
169+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=my-custom-serializer]
170+
----
171+
<1> Inherit from `SystemTextJsonSerializer`.
172+
<2> In the constructor, use the factory method `DefaultSourceSerializer.CreateDefaultJsonSerializerOptions` to create default options for serialization. No default converters are registered at this stage because we pass `false` as an argument.
173+
<3> Register our `CustomerTypeConverter` as the first converter.
174+
<4> To apply any default converters, call the `DefaultSourceSerializer.AddDefaultConverters` helper method, passing the options to modify.
175+
<5> Implement the `CreateJsonSerializerOptions` method returning the stored `JsonSerializerOptions`.
176+
177+
Because we have registered our `CustomerTypeConverter` before the default converters (which include the `JsonStringEnumConverter`), our converter takes precedence when serializing `CustomerType` instances on source documents.
178+
179+
The base `SystemTextJsonSerializer` class handles the implementation details of binding, which is required to ensure that the built-in converters can access the `IElasticsearchClientSettings` where needed.
180+
181+
We can then index a customer document into {es}.
182+
183+
[source,csharp]
184+
----
185+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings]
186+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=index-customer-without-jsonconverter-attribute]
187+
----
188+
189+
The `Customer` instance is serialized using the custom `enum` converter, creating the following JSON document.
190+
191+
[source,javascript]
192+
----
193+
{
194+
"customerName": "Customer Ltd",
195+
"customerType": "premium" // <1>
196+
}
197+
----
198+
<1> The string value applied during serialization is provided by our custom converter.
199+
200+
[discrete]
201+
[[creating-custom-serializers]]
202+
===== Creating a custom `Serializer`
203+
204+
Suppose you prefer using an alternative JSON serialization library for your source types. In that case, you can inject an isolated serializer only to be called for the serialization of `_source`, `_fields`, or wherever a user-provided value is expected to be written and returned.
205+
206+
Implementing `Elastic.Transport.Serializer` is technically enough to create a custom source serializer.
207+
208+
[source,csharp]
209+
----
210+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=vanilla-serializer-using-directives]
211+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=vanilla-serializer]
212+
----
213+
214+
Registering up the serializer is performed in the `ConnectionSettings` constructor.
215+
216+
[source,csharp]
217+
----
218+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=usings]
219+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=register-vanilla-serializer]
220+
----
221+
<1> If implementing `Serializer` is enough, why must we provide an instance wrapped in a factory `Func`?
222+
223+
There are various cases where you might have a POCO type that contains an `Elastic.Clients.Elasticsearch` type as one of its properties. The `SourceSerializerFactory` delegate provides access to the default built-in serializer so you can access it when necessary. For example, consider if you want to use percolation; you need to store {es} queries as part of the `_source` of your document, which means you need to have a POCO that looks like this.
224+
225+
[source,csharp]
226+
----
227+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=querydsl-using-directives]
228+
include::{doc-tests-src}/ClientConcepts/Serialization/CustomSerializationTests.cs[tag=percolation-document]
229+
----
230+
231+
A custom serializer would not know how to serialize `Query` or other `Elastic.Clients.Elasticsearch` types that could appear as part of
232+
the `_source` of a document. Therefore, your custom `Serializer` would need to store a reference to our built-in serializer and delegate serialization of Elastic types back to it.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[[modeling-documents-with-types]]
2+
==== Modeling documents with types
3+
4+
{es} provides search and aggregation capabilities on the documents that it is sent and indexes. These documents are sent as
5+
JSON objects within the request body of a HTTP request. It is natural to model documents within the {es} .NET client using
6+
https://en.wikipedia.org/wiki/Plain_Old_CLR_Object[POCOs (__Plain Old CLR Objects__)].
7+
8+
This section provides an overview of how types and type hierarchies can be used to model documents.
9+
10+
[[default-behaviour]]
11+
===== Default behaviour
12+
13+
The default behaviour is to serialize type property names as camelcase JSON object members.
14+
15+
We can model documents using a regular class (POCO).
16+
17+
[source,csharp]
18+
----
19+
include-tagged::{doc-tests-src}/ClientConcepts/Serialization/ModellingDocumentsWithTypesTests.cs[my-document-poco]
20+
----
21+
22+
We can then index the an instance of the document into {es}.
23+
24+
[source,csharp]
25+
----
26+
include-tagged::{doc-tests-src}/ClientConcepts/Serialization/ModellingDocumentsWithTypesTests.cs[usings]
27+
include-tagged::{doc-tests-src}/ClientConcepts/Serialization/ModellingDocumentsWithTypesTests.cs[index-my-document]
28+
----
29+
30+
The index request is serialized, with the source serializer handling the `MyDocument` type, serializing the POCO property named `StringProperty` to the JSON object member named `stringProperty`.
31+
32+
[source,javascript]
33+
----
34+
{
35+
"stringProperty": "value"
36+
}
37+
----

0 commit comments

Comments
 (0)