diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs index 48fdd7bd2e5..e56183774f2 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs @@ -46,7 +46,6 @@ public DefaultRequestResponseSerializer(IElasticsearchClientSettings settings) new SelfDeserializableConverterFactory(settings), new SelfTwoWaySerializableConverterFactory(settings), new IndicesJsonConverter(settings), - new PropertyNameConverter(settings), new IdsConverter(settings), new IsADictionaryConverter(), new ResponseItemConverterFactory(), diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs index 4d42c461f3d..a51a502629d 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs @@ -20,7 +20,6 @@ public DefaultSourceSerializer(IElasticsearchClientSettings settings, JsonSerial new IdConverter(settings), new RelationNameConverter(settings), new RoutingConverter(settings), - new PropertyNameConverter(settings), new JoinFieldConverter(settings), new LazyDocumentConverter(settings), new IdsConverter(settings) diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/PropertyNameConverter.cs b/src/Elastic.Clients.Elasticsearch/Serialization/PropertyNameConverter.cs index a4a816e2adb..2436f62a04c 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/PropertyNameConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/PropertyNameConverter.cs @@ -5,14 +5,17 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Elastic.Transport; namespace Elastic.Clients.Elasticsearch { - internal sealed class PropertyNameConverter : JsonConverter + internal sealed class PropertyNameConverter : SettingsJsonConverter { - private readonly IElasticsearchClientSettings _settings; + public override void WriteAsPropertyName(Utf8JsonWriter writer, PropertyName value, JsonSerializerOptions options) => + writer.WritePropertyName(((IUrlParameter)value).GetString(GetSettings(options))); - public PropertyNameConverter(IElasticsearchClientSettings settings) => _settings = settings; + public override PropertyName ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.GetString(); public override PropertyName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -33,7 +36,7 @@ public override void Write(Utf8JsonWriter writer, PropertyName? value, JsonSeria return; } - var propertyName = _settings.Inferrer.PropertyName(value); + var propertyName = GetSettings(options).Inferrer.PropertyName(value); writer.WriteStringValue(propertyName); } } diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/SettingsJsonConverter.cs b/src/Elastic.Clients.Elasticsearch/Serialization/SettingsJsonConverter.cs new file mode 100644 index 00000000000..2d8b1c1dadd --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Serialization/SettingsJsonConverter.cs @@ -0,0 +1,40 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Elastic.Clients.Elasticsearch +{ + /// + /// Used for derived converters which need access to in order to serialize. + /// + /// The type of object or value handled by the converter. + internal abstract class SettingsJsonConverter : JsonConverter + { + private IElasticsearchClientSettings _settings; + + /// + /// Access the for a given instance. + /// + /// The for which the should be retrieved. + /// An instance related to the provided . + /// Thrown if a corresponding instance cannot be found for the . + protected IElasticsearchClientSettings GetSettings(JsonSerializerOptions options) + { + if (_settings is not null) + return _settings; + + // We avoid locking here as the result of accessing the settings should be idemopotent and low cost. + + if (options.TryGetClientSettings(out var settings)) + { + _settings = settings; + return _settings; + } + + throw new JsonException("Unable to retrieve ElasticsearchClientSettings settings from JsonSerializerOptions."); + } + } +} diff --git a/src/Elastic.Clients.Elasticsearch/Types/Mapping/PropertyBase.cs b/src/Elastic.Clients.Elasticsearch/Types/Mapping/PropertyBase.cs index b931180131d..4250fa12c69 100644 --- a/src/Elastic.Clients.Elasticsearch/Types/Mapping/PropertyBase.cs +++ b/src/Elastic.Clients.Elasticsearch/Types/Mapping/PropertyBase.cs @@ -40,5 +40,16 @@ internal sealed partial class PropertyInterfaceConverter : JsonConverter throw new NotImplementedException(); + public override void Write(Utf8JsonWriter writer, IProperty value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + return; + } + + var type = value.GetType(); + + JsonSerializer.Serialize(writer, value, type, options); + } } diff --git a/tests/Tests/Serialization/Mapping/PropertiesSerializationTests.cs b/tests/Tests/Serialization/Mapping/PropertiesSerializationTests.cs new file mode 100644 index 00000000000..c6c7d435a84 --- /dev/null +++ b/tests/Tests/Serialization/Mapping/PropertiesSerializationTests.cs @@ -0,0 +1,42 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Elastic.Clients.Elasticsearch.Mapping; +using Tests.Domain; +using VerifyXunit; + +namespace Tests.Serialization.Mapping; + +[UsesVerify] +public class PropertiesSerializationTests : SerializerTestBase +{ + [U] + public async Task CanSerialize_Properties_WithPropertyNameExpression() + { + var result = await RoundTripAndVerifyJson(new Properties + { + { p => p.Name, new TextProperty { Boost = 1.2 } } + }); + + var textProperty = result["name"].Should().BeOfType().Subject; + textProperty.Boost.Should().Be(1.2); + } + + [U] + public async Task CanSerialize_MultipleProperties_WithPropertyNameExpression() + { + var result = await RoundTripAndVerifyJson(new Properties + { + { p => p.Name, new TextProperty { Boost = 1.2 } }, + { p => p.Description, new TextProperty { Boost = 1.4 } } + }); + + var nameTextProperty = result["name"].Should().BeOfType().Subject; + nameTextProperty.Boost.Should().Be(1.2); + + var descriptionTextProperty = result["description"].Should().BeOfType().Subject; + descriptionTextProperty.Boost.Should().Be(1.4); + } +} diff --git a/tests/Tests/Serialization/SerializerTestBase.cs b/tests/Tests/Serialization/SerializerTestBase.cs index 3ee381f9c55..522a5dbf12e 100644 --- a/tests/Tests/Serialization/SerializerTestBase.cs +++ b/tests/Tests/Serialization/SerializerTestBase.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Text.Json; using System.Threading.Tasks; using Tests.Domain.Extensions; using VerifyXunit; @@ -129,6 +130,20 @@ static SerializerTestBase() protected static Inferrer Inferrer => _settings.Inferrer; + public static async Task SerializeAndVerifyJson(T data) + { + var serialisedJson = await SerializeAndGetJsonStringAsync(data); + await Verifier.VerifyJson(serialisedJson); + return serialisedJson; + } + + public static async Task RoundTripAndVerifyJson(T data) + { + var serialisedJson = await SerializeAndGetJsonStringAsync(data); + await Verifier.VerifyJson(serialisedJson); + return JsonSerializer.Deserialize(serialisedJson); + } + protected static Stream WrapInStream(string json) { var stream = new MemoryStream(); diff --git a/tests/Tests/_VerifySnapshots/PropertiesSerializationTests.CanSerialize_MultipleProperties_WithPropertyNameExpression.verified.txt b/tests/Tests/_VerifySnapshots/PropertiesSerializationTests.CanSerialize_MultipleProperties_WithPropertyNameExpression.verified.txt new file mode 100644 index 00000000000..84e8f50a0f4 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/PropertiesSerializationTests.CanSerialize_MultipleProperties_WithPropertyNameExpression.verified.txt @@ -0,0 +1,10 @@ +{ + description: { + boost: 1.4, + type: text + }, + name: { + boost: 1.2, + type: text + } +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/PropertiesSerializationTests.CanSerialize_Properties_WithPropertyNameExpression.verified.txt b/tests/Tests/_VerifySnapshots/PropertiesSerializationTests.CanSerialize_Properties_WithPropertyNameExpression.verified.txt new file mode 100644 index 00000000000..2440b3054dc --- /dev/null +++ b/tests/Tests/_VerifySnapshots/PropertiesSerializationTests.CanSerialize_Properties_WithPropertyNameExpression.verified.txt @@ -0,0 +1,6 @@ +{ + name: { + boost: 1.2, + type: text + } +} \ No newline at end of file