From f1d691ea35007897721a4ccc74ad7a8d96fd49d4 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 12 Aug 2025 14:33:40 +0200 Subject: [PATCH 1/7] Fix remaining AOT compiler errors --- .../Elastic.Clients.Elasticsearch.csproj | 2 +- .../_Shared/Next/JsonSerializerOptionsExtensions.cs | 10 +++++----- .../_Shared/Next/ObjectToInferredTypesConverter.cs | 11 ++++++----- src/Playground/Playground.csproj | 1 - src/Playground/Program.cs | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj b/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj index 13992fe296f..ef506742514 100644 --- a/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj +++ b/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Next/JsonSerializerOptionsExtensions.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Next/JsonSerializerOptionsExtensions.cs index d161c05b17f..a94097a25ed 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Next/JsonSerializerOptionsExtensions.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Next/JsonSerializerOptionsExtensions.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; - +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.Json.Serialization; @@ -13,6 +13,8 @@ namespace Elastic.Clients.Elasticsearch.Serialization; internal static partial class JsonSerializerOptionsExtensions { + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static JsonConverter GetConverter(this JsonSerializerOptions options, Type? markerType) { @@ -20,13 +22,9 @@ public static JsonConverter GetConverter(this JsonSerializerOptions option // to directly use converters like we do. // When getting a default generic converter from `JsonSerializerOptions` that are not read-only, a // `NotSupportedException` is thrown as soon as we call `converter.Read()` or `converter.Write()`. -#pragma warning disable IL2026, IL3050 options.MakeReadOnly(true); -#pragma warning restore IL2026, IL3050 -#pragma warning disable IL2026, IL3050 var rawConverter = options.GetConverter(markerType ?? typeof(T)); -#pragma warning restore IL2026, IL3050 var converter = (JsonConverter)(rawConverter is IMarkerTypeConverter markerTypeConverter ? markerTypeConverter.WrappedConverter @@ -35,6 +33,8 @@ public static JsonConverter GetConverter(this JsonSerializerOptions option return converter; } + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TContext GetContext(this JsonSerializerOptions options) { diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Next/ObjectToInferredTypesConverter.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Next/ObjectToInferredTypesConverter.cs index 0a0c0f7dd0a..f6e77bc8ccd 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Next/ObjectToInferredTypesConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Next/ObjectToInferredTypesConverter.cs @@ -3,14 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; namespace Elastic.Clients.Elasticsearch.Serialization; -internal sealed class ObjectToInferredTypesConverter : - JsonConverter +internal sealed class ObjectToInferredTypesConverter : JsonConverter { + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch @@ -26,12 +28,11 @@ JsonTokenType.String when reader.TryGetDateTimeOffset(out var value) => value, }; } + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { // TODO: Match `SourceMarker` values and delegate to the `SourceSerializer`. - -#pragma warning disable IL2026, IL3050 JsonSerializer.Serialize(writer, value, value.GetType(), options); -#pragma warning restore IL2026, IL3050 } } diff --git a/src/Playground/Playground.csproj b/src/Playground/Playground.csproj index 1d27a40460d..354e15ad258 100644 --- a/src/Playground/Playground.csproj +++ b/src/Playground/Playground.csproj @@ -13,7 +13,6 @@ - diff --git a/src/Playground/Program.cs b/src/Playground/Program.cs index 5990a7f8ed2..91f6fa64dee 100644 --- a/src/Playground/Program.cs +++ b/src/Playground/Program.cs @@ -37,7 +37,7 @@ Console.WriteLine(id); Console.WriteLine(idByType); // This still errors on AOT compilation -//Console.WriteLine(client.SourceSerializer.SerializeToString(person)); +Console.WriteLine(client.SourceSerializer.SerializeToString(person)); [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = "Can only annotate our implementation")] [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "Can only annotate our implementation")] From ae8ac86cb167af97c65944d64e49cf14f421aef1 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 12 Aug 2025 15:54:39 +0200 Subject: [PATCH 2/7] More AOT annotations --- .../_Shared/Next/ContextProvider.cs | 8 ++++---- .../_Shared/Next/SourceConverter.cs | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Next/ContextProvider.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Next/ContextProvider.cs index 8e0f373b4de..84d1820993e 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Next/ContextProvider.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Next/ContextProvider.cs @@ -33,11 +33,11 @@ public ContextProvider(TContext context) /// If no for is registered to the given /// instance. /// + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] public static TContext GetContext(JsonSerializerOptions options) { -#pragma warning disable IL2026, IL3050 if (options.GetConverter(typeof(Marker)) is not Converter provider) -#pragma warning restore IL2026, IL3050 { throw new InvalidOperationException($"No context provider for type '{typeof(TContext).Name}' is " + $"registered for the given 'JsonSerializerOptions' instance."); @@ -55,11 +55,11 @@ public static TContext GetContext(JsonSerializerOptions options) /// if the context was successfully retrieved from the given /// or , if not. /// + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] public static bool TryGetContext(JsonSerializerOptions options, [MaybeNullWhen(false)] out TContext context) { -#pragma warning disable IL2026, IL3050 if (options.GetConverter(typeof(Marker)) is not Converter provider) -#pragma warning restore IL2026, IL3050 { context = default; return false; diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Next/SourceConverter.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Next/SourceConverter.cs index f52b062454f..b89de8d84f1 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Next/SourceConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Next/SourceConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; @@ -50,12 +51,11 @@ public override bool CanConvert(Type typeToConvert) typeToConvert.GetGenericTypeDefinition() == typeof(SourceMarker<>); } + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var args = typeToConvert.GetGenericArguments(); -#pragma warning disable IL3050 // SourceMarker static constructor roots SourceMarkerConverter. - var converter = (JsonConverter)Activator.CreateInstance( typeof(SourceMarkerConverter<>).MakeGenericType(args[0]), BindingFlags.Instance | BindingFlags.Public, @@ -63,14 +63,11 @@ public override bool CanConvert(Type typeToConvert) args: [settings], culture: null)!; -#pragma warning restore IL3050 - return converter; } } -internal sealed class SourceConverter : - JsonConverter +internal sealed class SourceConverter : JsonConverter { private readonly IElasticsearchClientSettings _settings; @@ -79,11 +76,15 @@ public SourceConverter(IElasticsearchClientSettings settings) _settings = settings; } + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return _settings.SourceSerializer.Deserialize(ref reader); } + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { _settings.SourceSerializer.Serialize(value, writer); From 5f5cd3c8e5166b17e33be7c6eaa5b2ff0c119704 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 12 Aug 2025 15:57:08 +0200 Subject: [PATCH 3/7] Id and Routing converters need to be public in order to be used by static serializer contexts outside of the client. --- src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/Id.cs | 2 +- .../_Shared/Core/Infer/Id/IdConverter.cs | 4 ++-- .../_Shared/Core/Infer/JoinFieldRouting/Routing.cs | 2 +- .../_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/Id.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/Id.cs index be299083f95..2940f91cbcd 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/Id.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/Id.cs @@ -8,7 +8,7 @@ using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; - +using Elastic.Clients.Elasticsearch.Json; using Elastic.Transport; namespace Elastic.Clients.Elasticsearch; diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs index a6728cab761..133a3b51659 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs @@ -9,9 +9,9 @@ using Elastic.Clients.Elasticsearch.Serialization; using Elastic.Transport; -namespace Elastic.Clients.Elasticsearch; +namespace Elastic.Clients.Elasticsearch.Json; -internal sealed class IdConverter : JsonConverter +public sealed class IdConverter : JsonConverter { private IElasticsearchClientSettings? _settings; diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/Routing.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/Routing.cs index 22caa1b9cb0..0ce24b92387 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/Routing.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/Routing.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; - +using Elastic.Clients.Elasticsearch.Json; using Elastic.Transport; namespace Elastic.Clients.Elasticsearch; diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs index f7b4d0c6efa..83ef02269ec 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs @@ -8,9 +8,9 @@ using System.Text.Json.Serialization; using Elastic.Clients.Elasticsearch.Serialization; -namespace Elastic.Clients.Elasticsearch; +namespace Elastic.Clients.Elasticsearch.Json; -internal sealed class RoutingConverter : JsonConverter +public sealed class RoutingConverter : JsonConverter { private IElasticsearchClientSettings _settings; From 897c1f2aec2a01642ed68979043f867ba80fcea1 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 12 Aug 2025 16:54:05 +0200 Subject: [PATCH 4/7] Add Serializer context --- src/Playground/Person.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Playground/Person.cs b/src/Playground/Person.cs index c1107bcfba3..758c6be632d 100644 --- a/src/Playground/Person.cs +++ b/src/Playground/Person.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Elastic.Clients.Elasticsearch; +using Playground; namespace Playground { @@ -16,7 +18,7 @@ public class Person public string? FirstName { get; init; } public string? LastName { get; init; } public int? Age { get; init; } - public bool IsDeleted { get; init; } + public bool IsDeleted { get; init; } public Routing? Routing { get; init; } public Id Idv3 => "testing"; @@ -37,3 +39,8 @@ public class PersonV3 public Guid SecondaryId { get; set; } = Guid.NewGuid(); } } + + + +[JsonSerializable(typeof(Person))] +internal partial class ExampleJsonSerializerContext : JsonSerializerContext; From f1c47e5dc0b56fefb282fab4a6c2599495b3f326 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 12 Aug 2025 19:17:51 +0200 Subject: [PATCH 5/7] Add overload taking multiple IJsonTypeResolvers and ensure we use JsonTypeInfoResolver.Combine --- .../ElasticsearchClientSettings.cs | 23 ++++++++++++------- .../Serialization/DefaultSourceSerializer.cs | 19 +++++++++++---- src/Playground/Person.cs | 12 ++++------ src/Playground/Program.cs | 9 +++++--- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs index 9b5197a4b95..a8444286571 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs @@ -9,7 +9,7 @@ using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices; - +using System.Text.Json.Serialization.Metadata; using Elastic.Clients.Elasticsearch.Esql; using Elastic.Clients.Elasticsearch.Requests; using Elastic.Clients.Elasticsearch.Serialization; @@ -86,10 +86,12 @@ public ElasticsearchClientSettings(InMemoryRequestInvoker inMemoryTransportClien public ElasticsearchClientSettings( NodePool nodePool, IRequestInvoker requestInvoker, - SourceSerializerFactory sourceSerializer, - IPropertyMappingProvider propertyMappingProvider) : base(nodePool, requestInvoker, sourceSerializer, propertyMappingProvider) + SourceSerializerFactory? sourceSerializer, + IPropertyMappingProvider? propertyMappingProvider + ) : base(nodePool, requestInvoker, sourceSerializer, propertyMappingProvider) { } + } /// @@ -121,9 +123,10 @@ public abstract class ElasticsearchClientSettingsBase : protected ElasticsearchClientSettingsBase( NodePool nodePool, - IRequestInvoker requestInvoker, + IRequestInvoker? requestInvoker, ElasticsearchClientSettings.SourceSerializerFactory? sourceSerializerFactory, - IPropertyMappingProvider propertyMappingProvider) + IPropertyMappingProvider? propertyMappingProvider + ) : base(nodePool, requestInvoker, null, ElasticsearchClientProductRegistration.DefaultForElasticsearchClientsElasticsearch) { var requestResponseSerializer = new DefaultRequestResponseSerializer(this); @@ -131,11 +134,12 @@ protected ElasticsearchClientSettingsBase( UseThisRequestResponseSerializer = requestResponseSerializer; + _propertyMappingProvider = propertyMappingProvider ?? new DefaultPropertyMappingProvider(); _sourceSerializer = sourceSerializerFactory?.Invoke(sourceSerializer, this) ?? sourceSerializer; - _propertyMappingProvider = propertyMappingProvider ?? sourceSerializer as IPropertyMappingProvider ?? new DefaultPropertyMappingProvider(); _defaultFieldNameInferrer = _sourceSerializer.TryGetJsonSerializerOptions(out var options) ? p => options.PropertyNamingPolicy?.ConvertName(p) ?? p : p => p.ToCamelCase(); + _defaultIndices = new FluentDictionary(); _defaultRelationNames = new FluentDictionary(); _inferrer = new Inferrer(this); @@ -394,9 +398,12 @@ public abstract class ConnectionConfigurationBase : { private bool _includeServerStackTraceOnError; - protected ConnectionConfigurationBase(NodePool nodePool, IRequestInvoker requestInvoker, + protected ConnectionConfigurationBase( + NodePool nodePool, + IRequestInvoker? requestInvoker, Serializer? serializer, - ProductRegistration registration = null) + ProductRegistration? registration = null + ) : base(nodePool, requestInvoker, serializer, registration ?? new ElasticsearchProductRegistration(typeof(ElasticsearchClient))) { UserAgent(ConnectionConfiguration.DefaultUserAgent); diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs index 0173c0f20ae..a4c8bb304a0 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs @@ -10,6 +10,7 @@ using System.Text.Json.Serialization.Metadata; using Elastic.Transport; +using Elastic.Transport.Products.Elasticsearch; namespace Elastic.Clients.Elasticsearch.Serialization; @@ -37,7 +38,12 @@ public DefaultSourceSerializer(IElasticsearchClientSettings settings, ActionA custom to use. /// An optional to customize the default . public DefaultSourceSerializer(IElasticsearchClientSettings settings, IJsonTypeInfoResolver typeInfoResolver, Action? configureOptions = null) : - base(new DefaultSourceSerializerOptionsProvider(settings, typeInfoResolver, configureOptions)) + base(new DefaultSourceSerializerOptionsProvider(settings, [typeInfoResolver], configureOptions)) + { + } + + public DefaultSourceSerializer(IElasticsearchClientSettings settings, IJsonTypeInfoResolver[] typeInfoResolvers, Action? configureOptions = null) : + base(new DefaultSourceSerializerOptionsProvider(settings, typeInfoResolvers, configureOptions)) { } } @@ -68,11 +74,11 @@ public DefaultSourceSerializerOptionsProvider(IElasticsearchClientSettings setti /// The instance to which this serializer options will be linked. /// A custom to use. /// An optional to customize the default . - public DefaultSourceSerializerOptionsProvider(IElasticsearchClientSettings settings, IJsonTypeInfoResolver typeInfoResolver, Action? configureOptions = null) : + public DefaultSourceSerializerOptionsProvider(IElasticsearchClientSettings settings, IJsonTypeInfoResolver[] typeInfoResolvers, Action? configureOptions = null) : base( CreateDefaultBuiltInConverters(settings), null, - options => MutateOptions(options, typeInfoResolver ?? throw new ArgumentNullException(nameof(typeInfoResolver)), configureOptions) + options => MutateOptions(options, typeInfoResolvers, configureOptions) ) { } @@ -94,10 +100,13 @@ private static IReadOnlyCollection CreateDefaultBuiltInConverters [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] - private static void MutateOptions(JsonSerializerOptions options, IJsonTypeInfoResolver? typeInfoResolver, Action? configureOptions) + private static void MutateOptions(JsonSerializerOptions options, IJsonTypeInfoResolver[]? typeInfoResolvers, Action? configureOptions) { - options.TypeInfoResolver = typeInfoResolver ?? new DefaultJsonTypeInfoResolver(); + var resolvers = typeInfoResolvers ?? []; + options.TypeInfoResolver = JsonTypeInfoResolver.Combine( + [new DefaultJsonTypeInfoResolver(), ElasticsearchTransportSerializerContext.Default, .. resolvers] + ); options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; diff --git a/src/Playground/Person.cs b/src/Playground/Person.cs index 1d8ac6e7dec..e1fcf7bcffe 100644 --- a/src/Playground/Person.cs +++ b/src/Playground/Person.cs @@ -6,15 +6,17 @@ using System.Text.Json.Serialization; using Elastic.Clients.Elasticsearch; using Playground; -using Elastic.Clients.Elasticsearch.QueryDsl; namespace Playground { + [JsonSerializable(typeof(Person))] + internal partial class PlaygroundJsonSerializerContext : JsonSerializerContext; + public class Person { public int Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("id2")] + [JsonPropertyName("id2")] public Guid SecondaryId { get; set; } = Guid.NewGuid(); public string? FirstName { get; init; } public string? LastName { get; init; } @@ -25,7 +27,7 @@ public class Person public Id Idv3 => "testing"; //public Guid Routing { get; init; } = Guid.NewGuid(); - [System.Text.Json.Serialization.JsonIgnore] + [JsonIgnore] public string? Email { get; init; } [DataMember(Name = "STEVE")] @@ -33,8 +35,6 @@ public class Person public string Data { get; init; } = "NOTHING"; public DateTimeKind Enum { get; init; } - - public Query? Q { get; init; } } public class PersonV3 @@ -45,5 +45,3 @@ public class PersonV3 -[JsonSerializable(typeof(Person))] -internal partial class ExampleJsonSerializerContext : JsonSerializerContext; diff --git a/src/Playground/Program.cs b/src/Playground/Program.cs index 91f6fa64dee..89a68a397f3 100644 --- a/src/Playground/Program.cs +++ b/src/Playground/Program.cs @@ -4,13 +4,16 @@ using System.Diagnostics.CodeAnalysis; using Elastic.Clients.Elasticsearch; +using Elastic.Clients.Elasticsearch.Serialization; using Elastic.Transport; using Elastic.Transport.Extensions; -using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; - using Playground; -var settings = new ElasticsearchClientSettings(new Uri("https://primary.es.europe-west3.gcp.cloud.es.io")) +var pool = new SingleNodePool(new Uri("https://primary.es.europe-west3.gcp.cloud.es.io")); +var settings = new ElasticsearchClientSettings(pool, + sourceSerializer: (_, settings) => + new DefaultSourceSerializer(settings, [PlaygroundJsonSerializerContext.Default]) + ) .Authentication(new BasicAuthentication("elastic", "Oov35Wtxj5DzpZNzYAzFb0KZ")) .DisableDirectStreaming() .EnableDebugMode(cd => From 3bbd96b1ca7a740fa0b4b6c5d4accb64b31574a6 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 13 Aug 2025 11:27:44 +0200 Subject: [PATCH 6/7] remove ElasticsearchTransportSerializerContext from sourceserializer combine --- .../_Shared/Serialization/DefaultSourceSerializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs index a4c8bb304a0..a052038f814 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs @@ -105,7 +105,7 @@ private static void MutateOptions(JsonSerializerOptions options, IJsonTypeInfoRe var resolvers = typeInfoResolvers ?? []; options.TypeInfoResolver = JsonTypeInfoResolver.Combine( - [new DefaultJsonTypeInfoResolver(), ElasticsearchTransportSerializerContext.Default, .. resolvers] + [new DefaultJsonTypeInfoResolver(), .. resolvers] ); options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; From abe53d75375848b4ff0d8913c59f6a0fa55fce93 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 13 Aug 2025 12:03:03 +0200 Subject: [PATCH 7/7] revert back to injecting singular IJsonTypeResolver --- .../Serialization/DefaultSourceSerializer.cs | 19 +++++-------------- src/Playground/Program.cs | 3 ++- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs index a052038f814..0173c0f20ae 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs @@ -10,7 +10,6 @@ using System.Text.Json.Serialization.Metadata; using Elastic.Transport; -using Elastic.Transport.Products.Elasticsearch; namespace Elastic.Clients.Elasticsearch.Serialization; @@ -38,12 +37,7 @@ public DefaultSourceSerializer(IElasticsearchClientSettings settings, ActionA custom to use. /// An optional to customize the default . public DefaultSourceSerializer(IElasticsearchClientSettings settings, IJsonTypeInfoResolver typeInfoResolver, Action? configureOptions = null) : - base(new DefaultSourceSerializerOptionsProvider(settings, [typeInfoResolver], configureOptions)) - { - } - - public DefaultSourceSerializer(IElasticsearchClientSettings settings, IJsonTypeInfoResolver[] typeInfoResolvers, Action? configureOptions = null) : - base(new DefaultSourceSerializerOptionsProvider(settings, typeInfoResolvers, configureOptions)) + base(new DefaultSourceSerializerOptionsProvider(settings, typeInfoResolver, configureOptions)) { } } @@ -74,11 +68,11 @@ public DefaultSourceSerializerOptionsProvider(IElasticsearchClientSettings setti /// The instance to which this serializer options will be linked. /// A custom to use. /// An optional to customize the default . - public DefaultSourceSerializerOptionsProvider(IElasticsearchClientSettings settings, IJsonTypeInfoResolver[] typeInfoResolvers, Action? configureOptions = null) : + public DefaultSourceSerializerOptionsProvider(IElasticsearchClientSettings settings, IJsonTypeInfoResolver typeInfoResolver, Action? configureOptions = null) : base( CreateDefaultBuiltInConverters(settings), null, - options => MutateOptions(options, typeInfoResolvers, configureOptions) + options => MutateOptions(options, typeInfoResolver ?? throw new ArgumentNullException(nameof(typeInfoResolver)), configureOptions) ) { } @@ -100,13 +94,10 @@ private static IReadOnlyCollection CreateDefaultBuiltInConverters [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] - private static void MutateOptions(JsonSerializerOptions options, IJsonTypeInfoResolver[]? typeInfoResolvers, Action? configureOptions) + private static void MutateOptions(JsonSerializerOptions options, IJsonTypeInfoResolver? typeInfoResolver, Action? configureOptions) { - var resolvers = typeInfoResolvers ?? []; + options.TypeInfoResolver = typeInfoResolver ?? new DefaultJsonTypeInfoResolver(); - options.TypeInfoResolver = JsonTypeInfoResolver.Combine( - [new DefaultJsonTypeInfoResolver(), .. resolvers] - ); options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; diff --git a/src/Playground/Program.cs b/src/Playground/Program.cs index 89a68a397f3..402bd448a62 100644 --- a/src/Playground/Program.cs +++ b/src/Playground/Program.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; using Elastic.Clients.Elasticsearch; using Elastic.Clients.Elasticsearch.Serialization; using Elastic.Transport; @@ -12,7 +13,7 @@ var pool = new SingleNodePool(new Uri("https://primary.es.europe-west3.gcp.cloud.es.io")); var settings = new ElasticsearchClientSettings(pool, sourceSerializer: (_, settings) => - new DefaultSourceSerializer(settings, [PlaygroundJsonSerializerContext.Default]) + new DefaultSourceSerializer(settings, PlaygroundJsonSerializerContext.Default) ) .Authentication(new BasicAuthentication("elastic", "Oov35Wtxj5DzpZNzYAzFb0KZ")) .DisableDirectStreaming()