diff --git a/src/Elastic.Clients.Elasticsearch/Common/DateTimeUtil.cs b/src/Elastic.Clients.Elasticsearch/Common/DateTimeUtil.cs deleted file mode 100644 index fe871a83e55..00000000000 --- a/src/Elastic.Clients.Elasticsearch/Common/DateTimeUtil.cs +++ /dev/null @@ -1,14 +0,0 @@ -// 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; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Elastic.Clients.Elasticsearch; - -internal static class DateTimeUtil -{ - public static readonly DateTimeOffset UnixEpoch = new(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); -} diff --git a/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/Field.cs b/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/Field.cs index e302c62331e..8fbfbab995b 100644 --- a/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/Field.cs +++ b/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/Field.cs @@ -14,11 +14,18 @@ namespace Elastic.Clients.Elasticsearch; [JsonConverter(typeof(FieldConverter))] [DebuggerDisplay("{" + nameof(DebugDisplay) + ",nq}")] -public sealed class Field : IEquatable, IUrlParameter, IDictionaryKey +public sealed class Field : IEquatable, IUrlParameter { private readonly object _comparisonValue; private readonly Type _type; + // Pseudo and metadata fields + + public static Field IdField = new("_id"); + public static Field ScoreField = new("_score"); + public static Field KeyField = new("_key"); + public static Field CountField = new("_count"); + public Field(string name) : this(name, null, null) { } public Field(string name, double boost) : this(name, boost, null) { } @@ -183,8 +190,6 @@ public override bool Equals(object obj) } } - string IDictionaryKey.Key(IElasticsearchClientSettings settings) => GetStringCore(settings); - public static bool operator ==(Field x, Field y) => Equals(x, y); public static bool operator !=(Field x, Field y) => !Equals(x, y); diff --git a/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/FieldConverter.cs b/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/FieldConverter.cs index f6486ea919e..d806575c572 100644 --- a/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/FieldConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/Common/Infer/Field/FieldConverter.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Elastic.Transport; namespace Elastic.Clients.Elasticsearch; @@ -14,6 +15,10 @@ internal sealed class FieldConverter : JsonConverter public FieldConverter(IElasticsearchClientSettings settings) => _settings = settings; + public override void WriteAsPropertyName(Utf8JsonWriter writer, Field value, JsonSerializerOptions options) => writer.WritePropertyName(((IUrlParameter)value).GetString(_settings)); + + public override Field ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetString(); + public override Field? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { switch (reader.TokenType) diff --git a/src/Elastic.Clients.Elasticsearch/Common/Infer/Id/Id.cs b/src/Elastic.Clients.Elasticsearch/Common/Infer/Id/Id.cs index f5e2461a619..7b10365b9c3 100644 --- a/src/Elastic.Clients.Elasticsearch/Common/Infer/Id/Id.cs +++ b/src/Elastic.Clients.Elasticsearch/Common/Infer/Id/Id.cs @@ -109,39 +109,4 @@ public override int GetHashCode() public static bool operator !=(Id left, Id right) => !Equals(left, right); } - - internal sealed class IdConverter : JsonConverter - { - private readonly IElasticsearchClientSettings _settings; - - public IdConverter(IElasticsearchClientSettings settings) => _settings = settings; - - public override Id? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType == JsonTokenType.Number - ? new Id(reader.GetInt64()) - : new Id(reader.GetString()); - - public override void Write(Utf8JsonWriter writer, Id value, JsonSerializerOptions options) - { - if (value is null) - { - writer.WriteNullValue(); - return; - } - - if (value.Document is not null) - { - var documentId = _settings.Inferrer.Id(value.Document.GetType(), value.Document); - writer.WriteStringValue(documentId); - } - else if (value.LongValue.HasValue) - { - writer.WriteNumberValue(value.LongValue.Value); - } - else - { - writer.WriteStringValue(value.StringValue); - } - } - } } diff --git a/src/Elastic.Clients.Elasticsearch/Common/Infer/Id/IdConverter.cs b/src/Elastic.Clients.Elasticsearch/Common/Infer/Id/IdConverter.cs new file mode 100644 index 00000000000..6cd7abec2f4 --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Common/Infer/Id/IdConverter.cs @@ -0,0 +1,50 @@ +// 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; +using System.Text.Json; +using System.Text.Json.Serialization; +using Elastic.Transport; + +namespace Elastic.Clients.Elasticsearch +{ + internal sealed class IdConverter : JsonConverter + { + private readonly IElasticsearchClientSettings _settings; + + public IdConverter(IElasticsearchClientSettings settings) => _settings = settings; + + public override void WriteAsPropertyName(Utf8JsonWriter writer, Id value, JsonSerializerOptions options) => writer.WritePropertyName(((IUrlParameter)value).GetString(_settings)); + + public override Id ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetString(); + + public override Id? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType == JsonTokenType.Number + ? new Id(reader.GetInt64()) + : new Id(reader.GetString()); + + public override void Write(Utf8JsonWriter writer, Id value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + return; + } + + if (value.Document is not null) + { + var documentId = _settings.Inferrer.Id(value.Document.GetType(), value.Document); + writer.WriteStringValue(documentId); + } + else if (value.LongValue.HasValue) + { + writer.WriteNumberValue(value.LongValue.Value); + } + else + { + writer.WriteStringValue(value.StringValue); + } + } + } +} diff --git a/src/Elastic.Clients.Elasticsearch/Common/Infer/IndexName/IndexNameConverter.cs b/src/Elastic.Clients.Elasticsearch/Common/Infer/IndexName/IndexNameConverter.cs index b7bb189334d..5119ca62a4d 100644 --- a/src/Elastic.Clients.Elasticsearch/Common/Infer/IndexName/IndexNameConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/Common/Infer/IndexName/IndexNameConverter.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Elastic.Transport; namespace Elastic.Clients.Elasticsearch { @@ -17,6 +18,10 @@ internal class IndexNameConverter : JsonConverter public IndexNameConverter(IElasticsearchClientSettings settings) => _settings = settings; + public override void WriteAsPropertyName(Utf8JsonWriter writer, IndexName value, JsonSerializerOptions options) => writer.WritePropertyName(((IUrlParameter)value).GetString(_settings)); + + public override IndexName ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetString(); + public override IndexName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.String) diff --git a/src/Elastic.Clients.Elasticsearch/Common/Infer/PropertyName/PropertyName.cs b/src/Elastic.Clients.Elasticsearch/Common/Infer/PropertyName/PropertyName.cs index ac3c7a4a465..6e85ba3124a 100644 --- a/src/Elastic.Clients.Elasticsearch/Common/Infer/PropertyName/PropertyName.cs +++ b/src/Elastic.Clients.Elasticsearch/Common/Infer/PropertyName/PropertyName.cs @@ -13,7 +13,7 @@ namespace Elastic.Clients.Elasticsearch { [DebuggerDisplay("{" + nameof(DebugDisplay) + ",nq}")] [JsonConverter(typeof(PropertyNameConverter))] - public sealed class PropertyName : IEquatable, IUrlParameter, IDictionaryKey + public sealed class PropertyName : IEquatable, IUrlParameter { private readonly object _comparisonValue; private readonly Type _type; @@ -51,8 +51,6 @@ public PropertyName(PropertyInfo property) private string PropertyDebug => Property == null ? null : $"PropertyInfo: {Property.Name}"; private static int TypeHashCode { get; } = typeof(PropertyName).GetHashCode(); - string IDictionaryKey.Key(IElasticsearchClientSettings settings) => GetInferredString(settings); - public bool Equals(PropertyName other) => EqualsMarker(other); string IUrlParameter.GetString(ITransportConfiguration? settings) diff --git a/src/Elastic.Clients.Elasticsearch/Common/Request/PlainRequestBase.cs b/src/Elastic.Clients.Elasticsearch/Common/Request/PlainRequestBase.cs index 65deaf40caf..e977690f3bc 100644 --- a/src/Elastic.Clients.Elasticsearch/Common/Request/PlainRequestBase.cs +++ b/src/Elastic.Clients.Elasticsearch/Common/Request/PlainRequestBase.cs @@ -7,87 +7,6 @@ namespace Elastic.Clients.Elasticsearch { - //public readonly partial struct PropertyName : IDictionaryKey - //{ - // public string Key => Value; - //} - - //// This is an incomplete stub implementation and should really be a struct - //public partial class Indices : IUrlParameter - //{ - // public static readonly Indices All = new("_all"); - - // internal Indices(IndexName index) => _indexNameList.Add(index); - - // public Indices(IEnumerable indices) - // { - // indices.ThrowIfEmpty(nameof(indices)); - // _indexNameList.AddRange(indices); - // } - - // public Indices(IEnumerable indices) - // { - // indices.ThrowIfEmpty(nameof(indices)); - // _indexNameList.AddRange(indices.Select(s => (IndexName)s)); - // } - - // public IReadOnlyCollection Values => _indexNameList.ToArray(); - - // public static Indices Parse(string names) => names.IsNullOrEmptyCommaSeparatedList(out var list) ? null : new Indices(list); - - // public static Indices Single(string index) => new Indices((IndexName)index); - - // public static implicit operator Indices(string names) => Parse(names); - - // string IUrlParameter.GetString(ITransportConfiguration? settings) - // { - // if (settings is not IElasticsearchClientSettings elasticsearchClientSettings) - // throw new Exception( - // "Tried to pass index names on query sting but it could not be resolved because no Elastic.Clients.Elasticsearch settings are available."); - - // var indices = _indexNameList.Select(i => i.GetString(settings)).Distinct(); - - // return string.Join(",", indices); - // } - //} - - //public partial struct IndicesList : IUrlParameter - //{ - // //public static readonly IndicesList All = new("_all"); - - // private readonly List _indices = new(); - - // internal IndicesList(IndexName index) => _indices.Add(index); - - // public IndicesList(IEnumerable indices) - // { - // indices.ThrowIfEmpty(nameof(indices)); - - // // De-duplicating during creation avoids cost when accessing the values. - // foreach (var index in indices) - // if (!_indices.Contains(index)) - // _indices.Add(index); - // } - - // public IndicesList(string[] indices) - // { - // indices.ThrowIfEmpty(nameof(indices)); - - // foreach (var index in indices) - // if (!_indices.Contains(index)) - // _indices.Add(index); - // } - - // public IReadOnlyCollection Values => _indices; - - // public static IndicesList Parse(string names) => names.IsNullOrEmptyCommaSeparatedList(out var list) ? null : new IndicesList(list); - - // public static implicit operator IndicesList(string names) => Parse(names); - //} - - //public partial struct IndicesList { string IUrlParameter.GetString(ITransportConfiguration? settings) => ""; } - - public abstract partial class PlainRequestBase { ///Include the stack trace of returned errors. diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs index 6f3d41b0a65..97e656c7e2d 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs @@ -37,6 +37,7 @@ public DefaultRequestResponseSerializer(IElasticsearchClientSettings settings) IncludeFields = true, Converters = { + new KeyValuePairConverterFactory(settings), new SourceConverterFactory(settings), new ReadOnlyIndexNameDictionaryConverterFactory(settings), new CalendarIntervalConverter(), @@ -55,11 +56,11 @@ public DefaultRequestResponseSerializer(IElasticsearchClientSettings settings) new SelfDeserializableConverterFactory(settings), new SelfTwoWaySerializableConverterFactory(settings), new IndicesJsonConverter(settings), - new DictionaryConverter(settings), new PropertyNameConverter(settings), new IsADictionaryConverter(), new ResponseItemConverterFactory(), - new UnionConverter() + new UnionConverter(), + new SingleOrManyConverterFactory(), }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs index 4eabc2b4b8f..d6259b350fd 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/DefaultSourceSerializer.cs @@ -16,7 +16,6 @@ public DefaultSourceSerializer(IElasticsearchClientSettings settings, JsonSerial Converters = { new JsonStringEnumConverter(), - new DictionaryConverter(settings), new UnionConverter(), new IdConverter(settings), new RelationNameConverter(settings), diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/DictionaryConverter.cs b/src/Elastic.Clients.Elasticsearch/Serialization/DictionaryConverter.cs deleted file mode 100644 index 948a895bf3d..00000000000 --- a/src/Elastic.Clients.Elasticsearch/Serialization/DictionaryConverter.cs +++ /dev/null @@ -1,169 +0,0 @@ -// 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; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Runtime.Serialization; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Elastic.Clients.Elasticsearch; - -internal sealed class DictionaryConverter : JsonConverterFactory -{ - private readonly IElasticsearchClientSettings _settings; - - public DictionaryConverter(IElasticsearchClientSettings settings) => _settings = settings; - - public override bool CanConvert(Type typeToConvert) - { - if (!typeToConvert.IsGenericType) - return false; - - return typeToConvert.GetGenericTypeDefinition() == typeof(Dictionary<,>); - } - - public override JsonConverter CreateConverter( - Type typeToConvert, - JsonSerializerOptions options) - { - var args = typeToConvert.GetGenericArguments(); - - var keyType = args[0]; - var valueType = args[1]; - - if (keyType.IsClass) - { - return (JsonConverter)Activator.CreateInstance( - typeof(DictionaryConverterInner<,>).MakeGenericType(keyType, valueType), _settings); - } - - return null; - } - - private class DictionaryConverterInner : JsonConverter> where TKey : class - { - private readonly JsonConverter? _valueConverter = null; - private readonly Type _valueType; - private readonly IElasticsearchClientSettings _settings; - - public DictionaryConverterInner(IElasticsearchClientSettings settings) - { - _valueType = typeof(TValue); - _settings = settings; - } - - public override Dictionary Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - if (reader.TokenType != JsonTokenType.StartObject) - throw new JsonException(); - var dictionary = new Dictionary(); - while (reader.Read()) - { - if (reader.TokenType == JsonTokenType.EndObject) - return dictionary; - - // Get the key. - if (reader.TokenType != JsonTokenType.PropertyName) - throw new JsonException(); - - var keyValue = reader.GetString(); - - if (keyValue is null) - throw new JsonException("Key was null."); - - // TODO: This is all very basic - TKey key; - - if (typeof(TKey) == typeof(string)) - { - key = (TKey)Activator.CreateInstance(typeof(string), keyValue.ToCharArray()); - } - else if (typeof(TKey) == typeof(IndexName)) - { - key = IndexName.Parse(keyValue) as TKey; - } - else if (typeof(TKey) == typeof(Field)) - { - key = new Field(keyValue) as TKey; - } - else if (typeof(TKey) == typeof(TaskId)) - { - key = new TaskId(keyValue) as TKey; - } - else if (typeof(TKey) == typeof(PropertyName)) - { - key = new PropertyName(keyValue) as TKey; - } - else - { - throw new JsonException("Unsupported dictionary key"); - //key = (TKey)Activator.CreateInstance(typeof(TKey), - // BindingFlags.Instance, - // null, - // new object[] { propertyName }, - // null); - } - - // Get the value. - TValue value; - if (_valueConverter != null) - { - reader.Read(); - value = _valueConverter.Read(ref reader, _valueType, options); - } - else - value = JsonSerializer.Deserialize(ref reader, options); - - // Add to dictionary. - dictionary.Add(key, value); - } - - return dictionary; - } - - public override void Write( - Utf8JsonWriter writer, - Dictionary dictionary, - JsonSerializerOptions options) - { - writer.WriteStartObject(); - - foreach (var item in dictionary) - { - if (item.Key is null) - throw new JsonException("Null key"); - - var propertyName = item.Key switch - { - string stringKey => stringKey, - IDictionaryKey key => key.Key(_settings), - _ => throw new JsonException("Must implement IDictionaryKey") - }; - - writer.WritePropertyName - (options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName); - - if (item.Value is null) - { - writer.WriteNullValue(); - } - else if (_valueConverter != null) - { - _valueConverter.Write(writer, item.Value, options); - } - else - { - JsonSerializer.Serialize(writer, item.Value, item.Value.GetType(), options); - } - } - - writer.WriteEndObject(); - } - } -} diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/InterfaceConverter.cs b/src/Elastic.Clients.Elasticsearch/Serialization/InterfaceConverter.cs index d4cb29b0c02..1327ce2c9bf 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/InterfaceConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/InterfaceConverter.cs @@ -6,15 +6,14 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Elastic.Clients.Elasticsearch +namespace Elastic.Clients.Elasticsearch; + +internal sealed class InterfaceConverter : JsonConverter + where TConcrete : class, TInterface { - internal sealed class InterfaceConverter : JsonConverter - where TConcrete : class, TInterface - { - public override TInterface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - JsonSerializer.Deserialize(ref reader, options); + public override TInterface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + JsonSerializer.Deserialize(ref reader, options); - public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options) => - JsonSerializer.Serialize(writer, value, value.GetType(), options); - } + public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options) => + JsonSerializer.Serialize(writer, value, value.GetType(), options); } diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/KeyValuePairConverter.cs b/src/Elastic.Clients.Elasticsearch/Serialization/KeyValuePairConverter.cs new file mode 100644 index 00000000000..59b4a6e4890 --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Serialization/KeyValuePairConverter.cs @@ -0,0 +1,67 @@ +// 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; +using System.Collections.Generic; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using Elastic.Transport; + +namespace Elastic.Clients.Elasticsearch +{ + internal sealed class KeyValuePairConverterFactory : JsonConverterFactory + { + private readonly IElasticsearchClientSettings _settings; + + public KeyValuePairConverterFactory(IElasticsearchClientSettings settings) => _settings = settings; + + public override bool CanConvert(Type typeToConvert) => typeToConvert.IsGenericType + && typeToConvert.Name == typeof(KeyValuePair<,>).Name + && typeof(IUrlParameter).IsAssignableFrom(typeToConvert.GetGenericArguments()[0]); + + public override JsonConverter CreateConverter( + Type type, + JsonSerializerOptions options) + { + var itemOneType = type.GetGenericArguments()[0]; + var itemTwoType = type.GetGenericArguments()[1]; + + return (JsonConverter)Activator.CreateInstance(typeof(KeyValuePairConverter<,>).MakeGenericType(itemOneType, itemTwoType), _settings); + } + + private class KeyValuePairConverter : JsonConverter> where TItem1 : class, IUrlParameter + { + private readonly IElasticsearchClientSettings _settings; + + public KeyValuePairConverter(IElasticsearchClientSettings settings) => _settings = settings; + + public override KeyValuePair Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Unexpected token for KeyValuePair"); + + reader.Read(); // property name (key) + var keyString = reader.GetString(); + + reader.Read(); // value + var value = JsonSerializer.Deserialize(ref reader, options); + + reader.Read(); // end object + + var key = (TItem1)Activator.CreateInstance(typeof(TItem1), keyString); + return new KeyValuePair(key, value); + } + + public override void Write(Utf8JsonWriter writer, KeyValuePair value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName(value.Key.GetString(_settings)); + JsonSerializer.Serialize(writer, value.Value, options); + writer.WriteEndObject(); + } + } + } +} diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/ResolvableDictionaryFormatterFactory.cs b/src/Elastic.Clients.Elasticsearch/Serialization/ResolvableDictionaryFormatterFactory.cs deleted file mode 100644 index 41c93a4c67b..00000000000 --- a/src/Elastic.Clients.Elasticsearch/Serialization/ResolvableDictionaryFormatterFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Elastic.Clients.Elasticsearch -{ - internal sealed class ResolvableDictionaryFormatterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => false; - - public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); - } -} diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/IDictionaryKey.cs b/src/Elastic.Clients.Elasticsearch/Serialization/SingleOrManyAttribute.cs similarity index 54% rename from src/Elastic.Clients.Elasticsearch/Serialization/IDictionaryKey.cs rename to src/Elastic.Clients.Elasticsearch/Serialization/SingleOrManyAttribute.cs index 8aacf81fb38..26c415d525c 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/IDictionaryKey.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/SingleOrManyAttribute.cs @@ -2,13 +2,15 @@ // 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; + namespace Elastic.Clients.Elasticsearch { - /// - /// Marker for types which may also act as a key in a dictionary. - /// - internal interface IDictionaryKey + [AttributeUsage(AttributeTargets.Class)] + internal sealed class SingleOrManyAttribute : Attribute { - string Key(IElasticsearchClientSettings settings); + public SingleOrManyAttribute(Type itemType) => ItemType = itemType; + + public Type ItemType { get; } } } diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/SingleOrManyConverterFactory.cs b/src/Elastic.Clients.Elasticsearch/Serialization/SingleOrManyConverterFactory.cs new file mode 100644 index 00000000000..5b596f0b7be --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Serialization/SingleOrManyConverterFactory.cs @@ -0,0 +1,87 @@ +// 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; +using System.Collections.Generic; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Elastic.Clients.Elasticsearch +{ + internal sealed class SingleOrManyConverterFactory : JsonConverterFactory + { + public override bool CanConvert(Type typeToConvert) + { + var customAttributes = typeToConvert.GetCustomAttributes(); + + var canConvert = false; + + foreach (var item in customAttributes) + { + var type = item.GetType(); + if (type == typeof(SingleOrManyAttribute)) + canConvert = true; + } + + return canConvert; + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var att = typeToConvert.GetCustomAttribute(); + return (JsonConverter)Activator.CreateInstance(typeof(SingleOrManyConverter<,>).MakeGenericType(typeToConvert, att.ItemType)); + } + + private class SingleOrManyConverter : JsonConverter where T : IList, new() + { + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.StartObject) + { + var singleItem = JsonSerializer.Deserialize(ref reader, options); + var list = new T(); + list.Add(singleItem); + return list; + } + + if (reader.TokenType == JsonTokenType.StartArray) + { + var list = new T(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + var item = JsonSerializer.Deserialize(ref reader, options); + list.Add(item); + } + return list; + } + + throw new JsonException("Unexpected token."); + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + if (value.Count == 0) + { + writer.WriteStartObject(); + writer.WriteEndObject(); + return; + } + + if (value.Count == 1) + { + JsonSerializer.Serialize(writer, value[0], options); + return; + } + + writer.WriteStartArray(); + foreach (var item in value) + { + JsonSerializer.Serialize(writer, item, options); + } + writer.WriteEndArray(); + } + } + } +} diff --git a/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregateOrder.cs b/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregateOrder.cs index 818a8fe8c5b..d9b09720120 100644 --- a/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregateOrder.cs +++ b/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregateOrder.cs @@ -6,23 +6,8 @@ namespace Elastic.Clients.Elasticsearch.Aggregations; public partial class AggregateOrder { - public static AggregateOrder KeyDescending => new(new System.Collections.Generic.Dictionary - { - { "_key", SortOrder.Desc } - }); - - public static AggregateOrder KeyAscending => new(new System.Collections.Generic.Dictionary - { - { "_key", SortOrder.Asc } - }); - - public static AggregateOrder CountDescending => new(new System.Collections.Generic.Dictionary - { - { "_count", SortOrder.Desc } - }); - - public static AggregateOrder CountAscending => new(new System.Collections.Generic.Dictionary - { - { "_count", SortOrder.Asc } - }); + public static AggregateOrder KeyDescending => new() { new System.Collections.Generic.KeyValuePair(Field.KeyField, SortOrder.Desc) }; + public static AggregateOrder KeyAscending => new() { new System.Collections.Generic.KeyValuePair(Field.KeyField, SortOrder.Asc) }; + public static AggregateOrder CountDescending => new() { new System.Collections.Generic.KeyValuePair(Field.CountField, SortOrder.Desc) }; + public static AggregateOrder CountAscending => new() { new System.Collections.Generic.KeyValuePair(Field.CountField, SortOrder.Asc) }; } diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregateOrder.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregateOrder.g.cs index 5c332dea816..7c8ea1b15de 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregateOrder.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregateOrder.g.cs @@ -17,6 +17,7 @@ using Elastic.Transport; using System; +using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Text.Json; @@ -25,14 +26,23 @@ #nullable restore namespace Elastic.Clients.Elasticsearch.Aggregations { - public partial class AggregateOrder : Union?, IReadOnlyCollection>?> + [SingleOrMany(typeof(KeyValuePair))] + public partial class AggregateOrder : IList> { - public AggregateOrder(Dictionary? item) : base(item) - { - } + private readonly IList> _backingList = new List>(); + public KeyValuePair this[int index] { get => _backingList[index]; set => _backingList[index] = value; } - public AggregateOrder(IReadOnlyCollection>? item) : base(item) - { - } + public int Count => _backingList.Count; + public bool IsReadOnly => _backingList.IsReadOnly; + public void Add(KeyValuePair item) => _backingList.Add(item); + public void Clear() => _backingList.Clear(); + public bool Contains(KeyValuePair item) => _backingList.Contains(item); + public void CopyTo(KeyValuePair[] array, int arrayIndex) => _backingList.CopyTo(array, arrayIndex); + public IEnumerator> GetEnumerator() => _backingList.GetEnumerator(); + public int IndexOf(KeyValuePair item) => _backingList.IndexOf(item); + public void Insert(int index, KeyValuePair item) => _backingList.Insert(index, item); + public bool Remove(KeyValuePair item) => _backingList.Remove(item); + public void RemoveAt(int index) => _backingList.RemoveAt(index); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_backingList).GetEnumerator(); } } \ No newline at end of file diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/TermsExclude.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/TermsExclude.g.cs index 6e5b0e11e1d..ff0b166df9c 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/TermsExclude.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/TermsExclude.g.cs @@ -17,6 +17,7 @@ using Elastic.Transport; using System; +using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Text.Json; @@ -25,8 +26,23 @@ #nullable restore namespace Elastic.Clients.Elasticsearch.Aggregations { - public partial class TermsExclude + [SingleOrMany(typeof(string))] + public partial class TermsExclude : IList { - private readonly List _stringList = new(); + private readonly IList _backingList = new List(); + public string this[int index] { get => _backingList[index]; set => _backingList[index] = value; } + + public int Count => _backingList.Count; + public bool IsReadOnly => _backingList.IsReadOnly; + public void Add(string item) => _backingList.Add(item); + public void Clear() => _backingList.Clear(); + public bool Contains(string item) => _backingList.Contains(item); + public void CopyTo(string[] array, int arrayIndex) => _backingList.CopyTo(array, arrayIndex); + public IEnumerator GetEnumerator() => _backingList.GetEnumerator(); + public int IndexOf(string item) => _backingList.IndexOf(item); + public void Insert(int index, string item) => _backingList.Insert(index, item); + public bool Remove(string item) => _backingList.Remove(item); + public void RemoveAt(int index) => _backingList.RemoveAt(index); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_backingList).GetEnumerator(); } } \ No newline at end of file diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Analysis/StopWords.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Analysis/StopWords.g.cs index 2c33552a011..fed85f568bc 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Analysis/StopWords.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Analysis/StopWords.g.cs @@ -17,6 +17,7 @@ using Elastic.Transport; using System; +using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Text.Json; @@ -25,8 +26,23 @@ #nullable restore namespace Elastic.Clients.Elasticsearch.Analysis { - public partial class StopWords + [SingleOrMany(typeof(string))] + public partial class StopWords : IList { - private readonly List _stringList = new(); + private readonly IList _backingList = new List(); + public string this[int index] { get => _backingList[index]; set => _backingList[index] = value; } + + public int Count => _backingList.Count; + public bool IsReadOnly => _backingList.IsReadOnly; + public void Add(string item) => _backingList.Add(item); + public void Clear() => _backingList.Clear(); + public bool Contains(string item) => _backingList.Contains(item); + public void CopyTo(string[] array, int arrayIndex) => _backingList.CopyTo(array, arrayIndex); + public IEnumerator GetEnumerator() => _backingList.GetEnumerator(); + public int IndexOf(string item) => _backingList.IndexOf(item); + public void Insert(int index, string item) => _backingList.Insert(index, item); + public bool Remove(string item) => _backingList.Remove(item); + public void RemoveAt(int index) => _backingList.RemoveAt(index); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_backingList).GetEnumerator(); } } \ No newline at end of file diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/ExpandWildcards.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/ExpandWildcards.g.cs index b55ce642147..833c54e495a 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/ExpandWildcards.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/ExpandWildcards.g.cs @@ -17,6 +17,7 @@ using Elastic.Transport; using System; +using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Text.Json; @@ -25,8 +26,23 @@ #nullable restore namespace Elastic.Clients.Elasticsearch { - public partial class ExpandWildcards + [SingleOrMany(typeof(Elastic.Clients.Elasticsearch.ExpandWildcard))] + public partial class ExpandWildcards : IList { - private readonly List _expandWildcardList = new(); + private readonly IList _backingList = new List(); + public Elastic.Clients.Elasticsearch.ExpandWildcard this[int index] { get => _backingList[index]; set => _backingList[index] = value; } + + public int Count => _backingList.Count; + public bool IsReadOnly => _backingList.IsReadOnly; + public void Add(Elastic.Clients.Elasticsearch.ExpandWildcard item) => _backingList.Add(item); + public void Clear() => _backingList.Clear(); + public bool Contains(Elastic.Clients.Elasticsearch.ExpandWildcard item) => _backingList.Contains(item); + public void CopyTo(Elastic.Clients.Elasticsearch.ExpandWildcard[] array, int arrayIndex) => _backingList.CopyTo(array, arrayIndex); + public IEnumerator GetEnumerator() => _backingList.GetEnumerator(); + public int IndexOf(Elastic.Clients.Elasticsearch.ExpandWildcard item) => _backingList.IndexOf(item); + public void Insert(int index, Elastic.Clients.Elasticsearch.ExpandWildcard item) => _backingList.Insert(index, item); + public bool Remove(Elastic.Clients.Elasticsearch.ExpandWildcard item) => _backingList.Remove(item); + public void RemoveAt(int index) => _backingList.RemoveAt(index); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_backingList).GetEnumerator(); } } \ No newline at end of file diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/Features.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/Features.g.cs index dfa96462fc3..13bb8ef1bd4 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/Features.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/Features.g.cs @@ -17,6 +17,7 @@ using Elastic.Transport; using System; +using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Text.Json; @@ -25,8 +26,23 @@ #nullable restore namespace Elastic.Clients.Elasticsearch.IndexManagement { - public partial class Features + [SingleOrMany(typeof(Elastic.Clients.Elasticsearch.IndexManagement.Feature))] + public partial class Features : IList { - private readonly List _featureList = new(); + private readonly IList _backingList = new List(); + public Elastic.Clients.Elasticsearch.IndexManagement.Feature this[int index] { get => _backingList[index]; set => _backingList[index] = value; } + + public int Count => _backingList.Count; + public bool IsReadOnly => _backingList.IsReadOnly; + public void Add(Elastic.Clients.Elasticsearch.IndexManagement.Feature item) => _backingList.Add(item); + public void Clear() => _backingList.Clear(); + public bool Contains(Elastic.Clients.Elasticsearch.IndexManagement.Feature item) => _backingList.Contains(item); + public void CopyTo(Elastic.Clients.Elasticsearch.IndexManagement.Feature[] array, int arrayIndex) => _backingList.CopyTo(array, arrayIndex); + public IEnumerator GetEnumerator() => _backingList.GetEnumerator(); + public int IndexOf(Elastic.Clients.Elasticsearch.IndexManagement.Feature item) => _backingList.IndexOf(item); + public void Insert(int index, Elastic.Clients.Elasticsearch.IndexManagement.Feature item) => _backingList.Insert(index, item); + public bool Remove(Elastic.Clients.Elasticsearch.IndexManagement.Feature item) => _backingList.Remove(item); + public void RemoveAt(int index) => _backingList.RemoveAt(index); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_backingList).GetEnumerator(); } } \ No newline at end of file diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/TextToAnalyze.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/TextToAnalyze.g.cs index 4a106c1ca9a..717ee9eed32 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/TextToAnalyze.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/TextToAnalyze.g.cs @@ -17,6 +17,7 @@ using Elastic.Transport; using System; +using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Text.Json; @@ -25,8 +26,23 @@ #nullable restore namespace Elastic.Clients.Elasticsearch.IndexManagement { - public partial class TextToAnalyze + [SingleOrMany(typeof(string))] + public partial class TextToAnalyze : IList { - private readonly List _stringList = new(); + private readonly IList _backingList = new List(); + public string this[int index] { get => _backingList[index]; set => _backingList[index] = value; } + + public int Count => _backingList.Count; + public bool IsReadOnly => _backingList.IsReadOnly; + public void Add(string item) => _backingList.Add(item); + public void Clear() => _backingList.Clear(); + public bool Contains(string item) => _backingList.Contains(item); + public void CopyTo(string[] array, int arrayIndex) => _backingList.CopyTo(array, arrayIndex); + public IEnumerator GetEnumerator() => _backingList.GetEnumerator(); + public int IndexOf(string item) => _backingList.IndexOf(item); + public void Insert(int index, string item) => _backingList.Insert(index, item); + public bool Remove(string item) => _backingList.Remove(item); + public void RemoveAt(int index) => _backingList.RemoveAt(index); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_backingList).GetEnumerator(); } } \ No newline at end of file diff --git a/tests/Tests/Serialization/AggregateOrderSerializationTests.cs b/tests/Tests/Serialization/AggregateOrderSerializationTests.cs new file mode 100644 index 00000000000..1f4fb4e5195 --- /dev/null +++ b/tests/Tests/Serialization/AggregateOrderSerializationTests.cs @@ -0,0 +1,66 @@ +// 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 Tests.Core.Xunit; +using VerifyXunit; +using Elastic.Clients.Elasticsearch.Aggregations; +using System.Collections.Generic; + +namespace Tests.Serialization; + +[UsesVerify] +[SystemTextJsonOnly] +public class AggregateOrderSerializationTests : SerializerTestBase +{ + [U] + public async Task SerializesWithSingleItem() + { + var aggregateOrder = AggregateOrder.CountAscending; + + var json = await SerializeAndGetJsonStringAsync(aggregateOrder); + + await Verifier.VerifyJson(json); + } + + [U] + public async Task SerializesWithMultipleItems() + { + var aggregateOrder = new AggregateOrder + { + new KeyValuePair("field-1", SortOrder.Asc), + new KeyValuePair("field-2", SortOrder.Desc) + }; + + var json = await SerializeAndGetJsonStringAsync(aggregateOrder); + + await Verifier.VerifyJson(json); + } + + [U] + public void DeserializesWithSingleItem() + { + var json = @"{""_count"":""asc""}"; + + var result = DeserializeJsonString(json); + + result.Should().HaveCount(1); + result[0].Key.Should().Be("_count"); + result[0].Value.Should().Be(SortOrder.Asc); + } + + [U] + public void DeserializesWithMultipleItems() + { + var json = @"[{""field-1"":""asc""},{""field-2"":""desc""}]"; + + var result = DeserializeJsonString(json); + + result.Should().HaveCount(2); + result[0].Key.Should().Be("field-1"); + result[0].Value.Should().Be(SortOrder.Asc); + result[1].Key.Should().Be("field-2"); + result[1].Value.Should().Be(SortOrder.Desc); + } +} diff --git a/tests/Tests/Serialization/BulkSerialisationTests.cs b/tests/Tests/Serialization/BulkSerialisationTests.cs index 0def0e2c760..a9e415e41fb 100644 --- a/tests/Tests/Serialization/BulkSerialisationTests.cs +++ b/tests/Tests/Serialization/BulkSerialisationTests.cs @@ -8,101 +8,100 @@ using Tests.Domain; using VerifyXunit; -namespace Tests.Serialization +namespace Tests.Serialization; + +[UsesVerify] +[SystemTextJsonOnly] +public class BulkSerialisationTests : SerializerTestBase { - [UsesVerify] - [SystemTextJsonOnly] - public class BulkSerialisationTests : SerializerTestBase + [U] + public async Task BulkRequest_SerialisationTest() { - [U] - public async Task BulkRequest_SerialisationTest() + var operations = new BulkOperationsCollection { - var operations = new BulkOperationsCollection - { - new BulkIndexOperation(FixedProject) { Index = "project" }, - new BulkIndexOperation(FixedProject) - }; + new BulkIndexOperation(FixedProject) { Index = "project" }, + new BulkIndexOperation(FixedProject) + }; - var request = new BulkRequest - { - Operations = operations - }; + var request = new BulkRequest + { + Operations = operations + }; - var serialisedJson = await SerializeAndGetJsonStringAsync(request); + var serialisedJson = await SerializeAndGetJsonStringAsync(request); - var sr = new StringReader(serialisedJson); - var count = 0; - while(true) + var sr = new StringReader(serialisedJson); + var count = 0; + while(true) + { + var line = sr.ReadLine(); + if (line is not null) { - var line = sr.ReadLine(); - if (line is not null) - { - await Verifier.VerifyJson(line).UseMethodName($"{nameof(BulkRequest_SerialisationTest)}_{++count}"); - } - else - { - break; - } + await Verifier.VerifyJson(line).UseMethodName($"{nameof(BulkRequest_SerialisationTest)}_{++count}"); + } + else + { + break; } } + } - [U] - public async Task BulkRequest_DescriptorSerialisationTest() - { - var request = new BulkRequestDescriptor(); - request.Index(FixedProject, b => b.Index("project")); - request.Index(FixedProject); + [U] + public async Task BulkRequest_DescriptorSerialisationTest() + { + var request = new BulkRequestDescriptor(); + request.Index(FixedProject, b => b.Index("project")); + request.Index(FixedProject); - var serialisedJson = await SerializeAndGetJsonStringAsync(request); + var serialisedJson = await SerializeAndGetJsonStringAsync(request); - var sr = new StringReader(serialisedJson); - var count = 0; - while (true) + var sr = new StringReader(serialisedJson); + var count = 0; + while (true) + { + var line = sr.ReadLine(); + if (line is not null) + { + await Verifier.VerifyJson(line).UseMethodName($"{nameof(BulkRequest_DescriptorSerialisationTest)}_{++count}"); + } + else { - var line = sr.ReadLine(); - if (line is not null) - { - await Verifier.VerifyJson(line).UseMethodName($"{nameof(BulkRequest_DescriptorSerialisationTest)}_{++count}"); - } - else - { - break; - } + break; } } + } - [U] - public async Task BulkRequest_IndexMany_DescriptorSerialisationTest() - { - var request = new BulkRequestDescriptor(); - request.IndexMany(new [] { FixedProject, FixedProject }); + [U] + public async Task BulkRequest_IndexMany_DescriptorSerialisationTest() + { + var request = new BulkRequestDescriptor(); + request.IndexMany(new [] { FixedProject, FixedProject }); - var serialisedJson = await SerializeAndGetJsonStringAsync(request); + var serialisedJson = await SerializeAndGetJsonStringAsync(request); - var sr = new StringReader(serialisedJson); - var count = 0; - while (true) + var sr = new StringReader(serialisedJson); + var count = 0; + while (true) + { + var line = sr.ReadLine(); + if (line is not null) { - var line = sr.ReadLine(); - if (line is not null) - { - await Verifier.VerifyJson(line).UseMethodName($"{nameof(BulkRequest_IndexMany_DescriptorSerialisationTest)}_{++count}"); - } - else - { - break; - } + await Verifier.VerifyJson(line).UseMethodName($"{nameof(BulkRequest_IndexMany_DescriptorSerialisationTest)}_{++count}"); } - } - - private static readonly Project FixedProject = new() - { - LeadDeveloper = new Developer + else { - FirstName = "Steve", - LastName = "Gordon", - Gender = Gender.Male + break; } - }; + } } + + private static readonly Project FixedProject = new() + { + LeadDeveloper = new Developer + { + FirstName = "Steve", + LastName = "Gordon", + Gender = Gender.Male + } + }; } diff --git a/tests/Tests/Serialization/KvpSerializationTests.cs b/tests/Tests/Serialization/KvpSerializationTests.cs new file mode 100644 index 00000000000..6d565da0011 --- /dev/null +++ b/tests/Tests/Serialization/KvpSerializationTests.cs @@ -0,0 +1,62 @@ +// 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.Collections.Generic; +using System.Threading.Tasks; +using Tests.Core.Xunit; +using VerifyXunit; + +namespace Tests.Serialization; + +[UsesVerify] +[SystemTextJsonOnly] +public class KvpSerializationTests : SerializerTestBase +{ + [U] + public async Task SerializesKvpWithFieldKey() + { + var dictionary = new KeyValuePair("field-name", new TestData { Name = "Steve", Age = 30 }); + + var json = await SerializeAndGetJsonStringAsync(dictionary); + + await Verifier.VerifyJson(json); + } + + [U] + public async Task SerializesKvp() + { + var dictionary = new KeyValuePair("field-name", 100); + + var json = await SerializeAndGetJsonStringAsync(dictionary); + + await Verifier.VerifyJson(json); + } + + [U] + public async Task DeserializesKvpWithFieldKey() + { + var json = @"{""field-name"":{""name"":""Steve"",""age"":30}}"; + + var result = DeserializeJsonString>(json); + + await Verifier.Verify(result); + } + + [U] + public async Task DeserializesKvp() + { + var json = @"{""key"": ""field-name"",""value"": 100}"; + + var result = DeserializeJsonString>(json); + + await Verifier.Verify(result); + } + + + private class TestData + { + public string Name { get; set; } + public int Age { get; set; } + } +} diff --git a/tests/Tests/_VerifySnapshots/AggregateOrderSerializationTests.SerializesWithMultipleItems.verified.txt b/tests/Tests/_VerifySnapshots/AggregateOrderSerializationTests.SerializesWithMultipleItems.verified.txt new file mode 100644 index 00000000000..8e4b036bc57 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/AggregateOrderSerializationTests.SerializesWithMultipleItems.verified.txt @@ -0,0 +1,8 @@ +[ + { + field-1: asc + }, + { + field-2: desc + } +] \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/AggregateOrderSerializationTests.SerializesWithSingleItem.verified.txt b/tests/Tests/_VerifySnapshots/AggregateOrderSerializationTests.SerializesWithSingleItem.verified.txt new file mode 100644 index 00000000000..1a8bb18ad16 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/AggregateOrderSerializationTests.SerializesWithSingleItem.verified.txt @@ -0,0 +1,3 @@ +{ + _count: asc +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/KvpSerializationTests.DeserializesKvp.verified.txt b/tests/Tests/_VerifySnapshots/KvpSerializationTests.DeserializesKvp.verified.txt new file mode 100644 index 00000000000..78682ec564b --- /dev/null +++ b/tests/Tests/_VerifySnapshots/KvpSerializationTests.DeserializesKvp.verified.txt @@ -0,0 +1,4 @@ +{ + Key: field-name, + Value: 100 +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/KvpSerializationTests.DeserializesKvpWithFieldKey.verified.txt b/tests/Tests/_VerifySnapshots/KvpSerializationTests.DeserializesKvpWithFieldKey.verified.txt new file mode 100644 index 00000000000..c0e0cc88fed --- /dev/null +++ b/tests/Tests/_VerifySnapshots/KvpSerializationTests.DeserializesKvpWithFieldKey.verified.txt @@ -0,0 +1,9 @@ +{ + Key: { + Name: field-name + }, + Value: { + Name: Steve, + Age: 30 + } +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/KvpSerializationTests.SerializesKvp.verified.txt b/tests/Tests/_VerifySnapshots/KvpSerializationTests.SerializesKvp.verified.txt new file mode 100644 index 00000000000..c48eff3595d --- /dev/null +++ b/tests/Tests/_VerifySnapshots/KvpSerializationTests.SerializesKvp.verified.txt @@ -0,0 +1,4 @@ +{ + key: field-name, + value: 100 +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/KvpSerializationTests.SerializesKvpWithFieldKey.verified.txt b/tests/Tests/_VerifySnapshots/KvpSerializationTests.SerializesKvpWithFieldKey.verified.txt new file mode 100644 index 00000000000..33eee8a2867 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/KvpSerializationTests.SerializesKvpWithFieldKey.verified.txt @@ -0,0 +1,6 @@ +{ + field-name: { + age: 30, + name: Steve + } +} \ No newline at end of file