1010using Nest ;
1111using Nest . JsonNetSerializer ;
1212using Newtonsoft . Json ;
13+ using Newtonsoft . Json . Linq ;
1314using Newtonsoft . Json . Serialization ;
1415using Tests . Framework ;
1516using static Tests . Core . Serialization . SerializationTestHelper ;
@@ -159,14 +160,11 @@ public void DefaultJsonNetSerializerFactoryMethods()
159160 * If you'd like to be more explicit, you can also derive from `ConnectionSettingsAwareSerializerBase`
160161 * and override the `CreateJsonSerializerSettings` and `ModifyContractResolver` methods
161162 */
162- public class MyCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
163+ public class MyFirstCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
163164 {
164- public MyCustomJsonNetSerializer ( IElasticsearchSerializer builtinSerializer , IConnectionSettingsValues connectionSettings )
165+ public MyFirstCustomJsonNetSerializer ( IElasticsearchSerializer builtinSerializer , IConnectionSettingsValues connectionSettings )
165166 : base ( builtinSerializer , connectionSettings ) { }
166167
167- protected override IEnumerable < JsonConverter > CreateJsonConverters ( ) =>
168- Enumerable . Empty < JsonConverter > ( ) ;
169-
170168 protected override JsonSerializerSettings CreateJsonSerializerSettings ( ) =>
171169 new JsonSerializerSettings
172170 {
@@ -176,6 +174,7 @@ protected override JsonSerializerSettings CreateJsonSerializerSettings() =>
176174 protected override void ModifyContractResolver ( ConnectionSettingsAwareContractResolver resolver ) =>
177175 resolver . NamingStrategy = new SnakeCaseNamingStrategy ( ) ;
178176 }
177+
179178 /**
180179 * Using `MyCustomJsonNetSerializer`, we can serialize using
181180 *
@@ -197,6 +196,13 @@ public class MyDocument
197196 public string FilePath { get ; set ; }
198197
199198 public int OwnerId { get ; set ; }
199+
200+ public IEnumerable < MySubDocument > SubDocuments { get ; set ; }
201+ }
202+
203+ public class MySubDocument
204+ {
205+ public string Name { get ; set ; }
200206 }
201207
202208 /**
@@ -208,7 +214,7 @@ [U] public void UsingJsonNetSerializer()
208214 var connectionSettings = new ConnectionSettings (
209215 pool ,
210216 connection : new InMemoryConnection ( ) , // <1> an _in-memory_ connection is used here for example purposes. In your production application, you would use an `IConnection` implementation that actually sends a request.
211- sourceSerializer : ( builtin , settings ) => new MyCustomJsonNetSerializer ( builtin , settings ) )
217+ sourceSerializer : ( builtin , settings ) => new MyFirstCustomJsonNetSerializer ( builtin , settings ) )
212218 . DefaultIndex ( "my-index" ) ;
213219
214220 //hide
@@ -233,7 +239,8 @@ [U] public void UsingJsonNetSerializer()
233239 id = 1 ,
234240 name = "My first document" ,
235241 file_path = ( string ) null ,
236- owner_id = 2
242+ owner_id = 2 ,
243+ sub_documents = ( object ) null
237244 } ;
238245 /**
239246 * which adheres to the conventions of our configured `MyCustomJsonNetSerializer` serializer.
@@ -242,5 +249,112 @@ [U] public void UsingJsonNetSerializer()
242249 // hide
243250 Expect ( expected , preserveNullInExpected : true ) . FromRequest ( indexResponse ) ;
244251 }
252+
253+ /** ==== Serializing Type Information
254+ * Here's another example that implements a custom contract resolver. The custom contract resolver
255+ * will include the type name within the serialized JSON for the document, which can be useful when
256+ * returning covariant document types within a collection.
257+ */
258+ public class MySecondCustomContractResolver : ConnectionSettingsAwareContractResolver
259+ {
260+ public MySecondCustomContractResolver ( IConnectionSettingsValues connectionSettings ) : base ( connectionSettings )
261+ {
262+ }
263+
264+ protected override JsonContract CreateContract ( Type objectType )
265+ {
266+ var contract = base . CreateContract ( objectType ) ;
267+ if ( contract is JsonContainerContract containerContract )
268+ {
269+ if ( containerContract . ItemTypeNameHandling == null )
270+ containerContract . ItemTypeNameHandling = TypeNameHandling . None ;
271+ }
272+
273+ return contract ;
274+ }
275+ }
276+
277+ public class MySecondCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
278+ {
279+ public MySecondCustomJsonNetSerializer ( IElasticsearchSerializer builtinSerializer , IConnectionSettingsValues connectionSettings )
280+ : base ( builtinSerializer , connectionSettings ) { }
281+
282+ protected override JsonSerializerSettings CreateJsonSerializerSettings ( ) =>
283+ new JsonSerializerSettings
284+ {
285+ TypeNameHandling = TypeNameHandling . All ,
286+ NullValueHandling = NullValueHandling . Ignore ,
287+ TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling . Simple
288+ } ;
289+
290+ protected override ConnectionSettingsAwareContractResolver CreateContractResolver ( ) =>
291+ new MySecondCustomContractResolver ( ConnectionSettings ) ; // <1> override the contract resolver
292+ }
293+
294+ /**
295+ * Now, hooking up this serializer
296+ */
297+ [ U ] public void MySecondJsonNetSerializer ( )
298+ {
299+ var pool = new SingleNodeConnectionPool ( new Uri ( "http://localhost:9200" ) ) ;
300+ var connectionSettings = new ConnectionSettings (
301+ pool ,
302+ connection : new InMemoryConnection ( ) ,
303+ sourceSerializer : ( builtin , settings ) => new MySecondCustomJsonNetSerializer ( builtin , settings ) )
304+ . DefaultIndex ( "my-index" ) ;
305+
306+ //hide
307+ connectionSettings . DisableDirectStreaming ( ) ;
308+
309+ var client = new ElasticClient ( connectionSettings ) ;
310+
311+ /** and indexing an instance of our document type */
312+ var document = new MyDocument
313+ {
314+ Id = 1 ,
315+ Name = "My first document" ,
316+ OwnerId = 2 ,
317+ SubDocuments = new [ ]
318+ {
319+ new MySubDocument { Name = "my first sub document" } ,
320+ new MySubDocument { Name = "my second sub document" } ,
321+ }
322+ } ;
323+
324+ var indexResponse = client . IndexDocument ( document ) ;
325+
326+ /** serializes to */
327+ //json
328+ var expected = new JObject
329+ {
330+ { "$type" , "Tests.ClientConcepts.HighLevel.Serialization.GettingStarted+MyDocument, Tests" } ,
331+ { "id" , 1 } ,
332+ { "name" , "My first document" } ,
333+ { "ownerId" , 2 } ,
334+ { "subDocuments" , new JArray
335+ {
336+ new JObject { { "name" , "my first sub document" } } ,
337+ new JObject { { "name" , "my second sub document" } } ,
338+ }
339+ }
340+ } ;
341+ /**
342+ * the type information is serialized for the outer `MyDocument` instance, but not for each
343+ * `MySubDocument` instance in the `SubDocuments` collection.
344+ *
345+ * When implementing a custom contract resolver derived from `ConnectionSettingsAwareContractResolver`,
346+ * be careful not to change the behaviour of the resolver for NEST types; doing so will result in
347+ * unexpected behaviour.
348+ *
349+ * [WARNING]
350+ * --
351+ * Per the https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm[Json.NET documentation on TypeNameHandling],
352+ * it should be used with caution when your application deserializes JSON from an external source.
353+ * --
354+ */
355+
356+ // hide
357+ Expect ( expected ) . FromRequest ( indexResponse ) ;
358+ }
245359 }
246360}
0 commit comments