From ec4f48e4255588ab22d7e49246566c89f7f2cb35 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 12 Dec 2024 09:54:17 -0500 Subject: [PATCH 01/19] rebase 1 --- .../EnumRepresentationConvention.cs | 50 +++++++++---------- .../EnumRepresentationConventionTests.cs | 33 ++++++++++++ 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 55ad9a00b9e..f9c33973a28 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -15,6 +15,7 @@ using System; using System.Reflection; +using MongoDB.Bson.Serialization.Serializers; namespace MongoDB.Bson.Serialization.Conventions { @@ -49,38 +50,37 @@ public EnumRepresentationConvention(BsonType representation) /// The member map. public void Apply(BsonMemberMap memberMap) { - var memberType = memberMap.MemberType; - var memberTypeInfo = memberType.GetTypeInfo(); + var serializer = memberMap.GetSerializer(); - if (memberTypeInfo.IsEnum) + if (memberMap.MemberType.IsEnum && serializer is IRepresentationConfigurable representationConfigurableSerializer) { - var serializer = memberMap.GetSerializer(); - var representationConfigurableSerializer = serializer as IRepresentationConfigurable; - if (representationConfigurableSerializer != null) - { - var reconfiguredSerializer = representationConfigurableSerializer.WithRepresentation(_representation); - memberMap.SetSerializer(reconfiguredSerializer); - } + memberMap.SetSerializer(representationConfigurableSerializer.WithRepresentation(_representation)); return; } - if (IsNullableEnum(memberType)) + var reconfiguredSerializer = Reconfigure(serializer); + if (reconfiguredSerializer is not null) { - var serializer = memberMap.GetSerializer(); - var childSerializerConfigurableSerializer = serializer as IChildSerializerConfigurable; - if (childSerializerConfigurableSerializer != null) - { - var childSerializer = childSerializerConfigurableSerializer.ChildSerializer; - var representationConfigurableChildSerializer = childSerializer as IRepresentationConfigurable; - if (representationConfigurableChildSerializer != null) - { - var reconfiguredChildSerializer = representationConfigurableChildSerializer.WithRepresentation(_representation); - var reconfiguredSerializer = childSerializerConfigurableSerializer.WithChildSerializer(reconfiguredChildSerializer); - memberMap.SetSerializer(reconfiguredSerializer); - } - } - return; + memberMap.SetSerializer(reconfiguredSerializer); + } + } + + private IBsonSerializer Reconfigure(IBsonSerializer serializer) + { + if (serializer is IChildSerializerConfigurable childSerializerConfigurable) + { + var childSerializer = childSerializerConfigurable.ChildSerializer; + var reconfiguredChildSerializer = Reconfigure(childSerializer); + return reconfiguredChildSerializer is null ? null : childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer); } + + var serializerType = serializer.GetType(); + if (serializerType.IsGenericType && serializerType.GetGenericTypeDefinition() == typeof(EnumSerializer<>)) + { + return (serializer as IRepresentationConfigurable)?.WithRepresentation(_representation); + } + + return null; } // private methods diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index bde58d6af76..520df844fee 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -14,6 +14,7 @@ */ using System; +using System.Collections.Generic; using System.Linq.Expressions; using FluentAssertions; using MongoDB.Bson.Serialization; @@ -31,7 +32,9 @@ public class C { public E E { get; set; } public E? NE { get; set; } + public E[] ArrayEnum { get; set; } public int I { get; set; } + public int[] ArrayInt { get; set; } } [Theory] @@ -51,6 +54,8 @@ public void Apply_should_configure_serializer_when_member_is_an_enum(BsonType re [Theory] [InlineData(BsonType.Int32)] [InlineData(BsonType.Int64)] + [InlineData(BsonType.String)] + //TODO we need a test like this for collections of various kinds (including dictionaries) public void Apply_should_configure_serializer_when_member_is_a_nullable_enum(BsonType representation) { var subject = new EnumRepresentationConvention(representation); @@ -63,6 +68,22 @@ public void Apply_should_configure_serializer_when_member_is_a_nullable_enum(Bso childSerializer.Representation.Should().Be(representation); } + [Theory] + [InlineData(BsonType.Int32)] + [InlineData(BsonType.Int64)] + [InlineData(BsonType.String)] + public void Apply_should_configure_serializer_when_member_is_an_enum_collection(BsonType representation) + { + var subject = new EnumRepresentationConvention(representation); + var memberMap = CreateMemberMap(c => c.ArrayEnum); + + subject.Apply(memberMap); + + var serializer = (IChildSerializerConfigurable)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)serializer.ChildSerializer; + childSerializer.Representation.Should().Be(representation); + } + [Fact] public void Apply_should_do_nothing_when_member_is_not_an_enum() { @@ -75,6 +96,18 @@ public void Apply_should_do_nothing_when_member_is_not_an_enum() memberMap.GetSerializer().Should().BeSameAs(serializer); } + [Fact] + public void Apply_should_do_nothing_when_member_is_not_an_enum_collection() + { + var subject = new EnumRepresentationConvention(BsonType.String); + var memberMap = CreateMemberMap(c => c.ArrayInt); + var serializer = memberMap.GetSerializer(); + + subject.Apply(memberMap); + + memberMap.GetSerializer().Should().BeSameAs(serializer); + } + [Theory] [InlineData((BsonType)0)] [InlineData(BsonType.Int32)] From 91daba729b27186f5ad2285b32fa5ea7354e147f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:15:15 +0100 Subject: [PATCH 02/19] Small fix plus added dictionary test --- .../EnumRepresentationConvention.cs | 17 ++--------------- .../EnumRepresentationConventionTests.cs | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index f9c33973a28..4a1f4b4df4b 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -14,7 +14,6 @@ */ using System; -using System.Reflection; using MongoDB.Bson.Serialization.Serializers; namespace MongoDB.Bson.Serialization.Conventions @@ -84,25 +83,13 @@ private IBsonSerializer Reconfigure(IBsonSerializer serializer) } // private methods - private bool IsNullableEnum(Type type) - { - return - type.GetTypeInfo().IsGenericType && - type.GetGenericTypeDefinition() == typeof(Nullable<>) && - Nullable.GetUnderlyingType(type).GetTypeInfo().IsEnum; - } - private void EnsureRepresentationIsValidForEnums(BsonType representation) { - if ( - representation == 0 || - representation == BsonType.String || - representation == BsonType.Int32 || - representation == BsonType.Int64) + if (representation is 0 or BsonType.String or BsonType.Int32 or BsonType.Int64) { return; } - throw new ArgumentException("Enums can only be represented as String, Int32, Int64 or the type of the enum", "representation"); + throw new ArgumentException("Enums can only be represented as String, Int32, Int64 or the type of the enum", nameof(representation)); } } } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index 520df844fee..d6fbc9a5531 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -33,6 +33,7 @@ public class C public E E { get; set; } public E? NE { get; set; } public E[] ArrayEnum { get; set; } + public Dictionary DictionaryEnum { get; set; } public int I { get; set; } public int[] ArrayInt { get; set; } } @@ -55,7 +56,6 @@ public void Apply_should_configure_serializer_when_member_is_an_enum(BsonType re [InlineData(BsonType.Int32)] [InlineData(BsonType.Int64)] [InlineData(BsonType.String)] - //TODO we need a test like this for collections of various kinds (including dictionaries) public void Apply_should_configure_serializer_when_member_is_a_nullable_enum(BsonType representation) { var subject = new EnumRepresentationConvention(representation); @@ -84,6 +84,22 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_collection( childSerializer.Representation.Should().Be(representation); } + [Theory] + [InlineData(BsonType.Int32)] + [InlineData(BsonType.Int64)] + [InlineData(BsonType.String)] + public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary(BsonType representation) + { + var subject = new EnumRepresentationConvention(representation); + var memberMap = CreateMemberMap(c => c.DictionaryEnum); + + subject.Apply(memberMap); + + var serializer = (IChildSerializerConfigurable)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)serializer.ChildSerializer; + childSerializer.Representation.Should().Be(representation); + } + [Fact] public void Apply_should_do_nothing_when_member_is_not_an_enum() { From cdce373358fd9de9fecf9eb475d46fc0584a3949 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:05:59 +0100 Subject: [PATCH 03/19] Added nested tests --- .../EnumRepresentationConventionTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index d6fbc9a5531..04375962c45 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -33,7 +33,9 @@ public class C public E E { get; set; } public E? NE { get; set; } public E[] ArrayEnum { get; set; } + public E[][] ArrayOfArrayEnum { get; set; } public Dictionary DictionaryEnum { get; set; } + public Dictionary NestedDictionaryEnum { get; set; } public int I { get; set; } public int[] ArrayInt { get; set; } } @@ -84,6 +86,22 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_collection( childSerializer.Representation.Should().Be(representation); } + [Theory] + [InlineData(BsonType.Int32)] + [InlineData(BsonType.Int64)] + [InlineData(BsonType.String)] + public void Apply_should_configure_serializer_when_member_is_a_nested_enum_collection(BsonType representation) + { + var subject = new EnumRepresentationConvention(representation); + var memberMap = CreateMemberMap(c => c.ArrayOfArrayEnum); + + subject.Apply(memberMap); + + var serializer = (IChildSerializerConfigurable)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)((IChildSerializerConfigurable)serializer.ChildSerializer).ChildSerializer; + childSerializer.Representation.Should().Be(representation); + } + [Theory] [InlineData(BsonType.Int32)] [InlineData(BsonType.Int64)] @@ -100,6 +118,22 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary( childSerializer.Representation.Should().Be(representation); } + [Theory] + [InlineData(BsonType.Int32)] + [InlineData(BsonType.Int64)] + [InlineData(BsonType.String)] + public void Apply_should_configure_serializer_when_member_is_a_nested_enum_dictionary(BsonType representation) + { + var subject = new EnumRepresentationConvention(representation); + var memberMap = CreateMemberMap(c => c.NestedDictionaryEnum); + + subject.Apply(memberMap); + + var serializer = (IChildSerializerConfigurable)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)((IChildSerializerConfigurable)serializer.ChildSerializer).ChildSerializer; + childSerializer.Representation.Should().Be(representation); + } + [Fact] public void Apply_should_do_nothing_when_member_is_not_an_enum() { From f8078536da8787c6f7280dbf2aca37f97369d04b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:53:09 +0100 Subject: [PATCH 04/19] Small improvement --- .../Conventions/EnumRepresentationConvention.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 4a1f4b4df4b..842509966d5 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -73,10 +73,9 @@ private IBsonSerializer Reconfigure(IBsonSerializer serializer) return reconfiguredChildSerializer is null ? null : childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer); } - var serializerType = serializer.GetType(); - if (serializerType.IsGenericType && serializerType.GetGenericTypeDefinition() == typeof(EnumSerializer<>)) + if (serializer.ValueType.IsEnum && serializer is IRepresentationConfigurable representationConfigurable) { - return (serializer as IRepresentationConfigurable)?.WithRepresentation(_representation); + return representationConfigurable.WithRepresentation(_representation); } return null; From d7b1739e1b30f3e17e31fc52515e6c896796a5aa Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:53:04 -0500 Subject: [PATCH 05/19] Corrections --- .../EnumRepresentationConvention.cs | 38 +++++++++++++++++-- .../EnumRepresentationConventionTests.cs | 38 ++++++++++++++++--- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 842509966d5..efcd06285a4 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -14,7 +14,7 @@ */ using System; -using MongoDB.Bson.Serialization.Serializers; +using System.Reflection; namespace MongoDB.Bson.Serialization.Conventions { @@ -25,6 +25,7 @@ public class EnumRepresentationConvention : ConventionBase, IMemberMapConvention { // private fields private readonly BsonType _representation; + private readonly bool _shouldApplyToCollections; // constructors /// @@ -33,9 +34,21 @@ public class EnumRepresentationConvention : ConventionBase, IMemberMapConvention /// The serialization representation. 0 is used to detect representation /// from the enum itself. public EnumRepresentationConvention(BsonType representation) + :this(representation, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The serialization representation. 0 is used to detect representation + /// from the enum itself. + /// If set to true, the convention will be applied also to collection of enums, recursively. + public EnumRepresentationConvention(BsonType representation, bool shouldApplyToCollections) { EnsureRepresentationIsValidForEnums(representation); _representation = representation; + _shouldApplyToCollections = shouldApplyToCollections; } /// @@ -43,6 +56,11 @@ public EnumRepresentationConvention(BsonType representation) /// public BsonType Representation => _representation; + /// + /// Gets a boolean indicating if this convention should be also applied to collections of enums. + /// + public bool ShouldApplyToCollections => _shouldApplyToCollections; + /// /// Applies a modification to the member map. /// @@ -57,10 +75,13 @@ public void Apply(BsonMemberMap memberMap) return; } - var reconfiguredSerializer = Reconfigure(serializer); - if (reconfiguredSerializer is not null) + if (_shouldApplyToCollections || IsNullableEnum(memberMap.MemberType)) { - memberMap.SetSerializer(reconfiguredSerializer); + var reconfiguredSerializer = Reconfigure(serializer); + if (reconfiguredSerializer is not null) + { + memberMap.SetSerializer(reconfiguredSerializer); + } } } @@ -82,6 +103,15 @@ private IBsonSerializer Reconfigure(IBsonSerializer serializer) } // private methods + // private methods + private bool IsNullableEnum(Type type) + { + return + type.GetTypeInfo().IsGenericType && + type.GetGenericTypeDefinition() == typeof(Nullable<>) && + Nullable.GetUnderlyingType(type)!.GetTypeInfo().IsEnum; + } + private void EnsureRepresentationIsValidForEnums(BsonType representation) { if (representation is 0 or BsonType.String or BsonType.Int32 or BsonType.Int64) diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index 04375962c45..827282020a5 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -20,6 +20,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Conventions; using MongoDB.Bson.Serialization.Serializers; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Bson.Tests.Serialization.Conventions @@ -76,7 +77,7 @@ public void Apply_should_configure_serializer_when_member_is_a_nullable_enum(Bso [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_an_enum_collection(BsonType representation) { - var subject = new EnumRepresentationConvention(representation); + var subject = new EnumRepresentationConvention(representation, true); var memberMap = CreateMemberMap(c => c.ArrayEnum); subject.Apply(memberMap); @@ -92,7 +93,7 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_collection( [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_a_nested_enum_collection(BsonType representation) { - var subject = new EnumRepresentationConvention(representation); + var subject = new EnumRepresentationConvention(representation, true); var memberMap = CreateMemberMap(c => c.ArrayOfArrayEnum); subject.Apply(memberMap); @@ -108,7 +109,7 @@ public void Apply_should_configure_serializer_when_member_is_a_nested_enum_colle [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary(BsonType representation) { - var subject = new EnumRepresentationConvention(representation); + var subject = new EnumRepresentationConvention(representation, true); var memberMap = CreateMemberMap(c => c.DictionaryEnum); subject.Apply(memberMap); @@ -124,7 +125,7 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary( [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_a_nested_enum_dictionary(BsonType representation) { - var subject = new EnumRepresentationConvention(representation); + var subject = new EnumRepresentationConvention(representation, true); var memberMap = CreateMemberMap(c => c.NestedDictionaryEnum); subject.Apply(memberMap); @@ -134,6 +135,21 @@ public void Apply_should_configure_serializer_when_member_is_a_nested_enum_dicti childSerializer.Representation.Should().Be(representation); } + [Theory] + [InlineData(BsonType.Int64)] + [InlineData(BsonType.String)] + public void Apply_should_do_nothing_when_member_is_an_enum_collection_and_should_apply_to_collection_is_false(BsonType representation) + { + var subject = new EnumRepresentationConvention(representation, false); + var memberMap = CreateMemberMap(c => c.ArrayEnum); + + subject.Apply(memberMap); + + var serializer = (IChildSerializerConfigurable)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)serializer.ChildSerializer; + childSerializer.Representation.Should().Be(BsonType.Int32); + } + [Fact] public void Apply_should_do_nothing_when_member_is_not_an_enum() { @@ -163,13 +179,25 @@ public void Apply_should_do_nothing_when_member_is_not_an_enum_collection() [InlineData(BsonType.Int32)] [InlineData(BsonType.Int64)] [InlineData(BsonType.String)] - public void constructor_should_initialize_instance_when_representation_is_valid(BsonType representation) + public void constructor_with_representation_should_return_expected_result(BsonType representation) { var subject = new EnumRepresentationConvention(representation); subject.Representation.Should().Be(representation); } + [Theory] + [ParameterAttributeData] + public void constructor_with_representation_and_should_apply_to_collection_should_return_expected_result( + [Values((BsonType)0, BsonType.Int32, BsonType.Int64, BsonType.String)] BsonType representation, + [Values(true, false)] bool shouldApplyToCollections) + { + var subject = new EnumRepresentationConvention(representation, shouldApplyToCollections); + + subject.Representation.Should().Be(representation); + subject.ShouldApplyToCollections.Should().Be(shouldApplyToCollections); + } + [Theory] [InlineData(BsonType.Decimal128)] [InlineData(BsonType.Double)] From 6fd00005ef9fb2db2dfdc314858a4eb63e2745b4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:27:48 +0100 Subject: [PATCH 06/19] Corrections according to PR --- .../Conventions/EnumRepresentationConvention.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index efcd06285a4..1b82f054d05 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -102,14 +102,13 @@ private IBsonSerializer Reconfigure(IBsonSerializer serializer) return null; } - // private methods // private methods private bool IsNullableEnum(Type type) { return - type.GetTypeInfo().IsGenericType && + type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && - Nullable.GetUnderlyingType(type)!.GetTypeInfo().IsEnum; + Nullable.GetUnderlyingType(type)!.IsEnum; } private void EnsureRepresentationIsValidForEnums(BsonType representation) From 5378c0ca337e008ea488334194083deb5527cfbb Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:36:18 +0100 Subject: [PATCH 07/19] Small fix --- .../Serialization/Conventions/EnumRepresentationConvention.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 1b82f054d05..80fbd8b87fb 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -14,7 +14,6 @@ */ using System; -using System.Reflection; namespace MongoDB.Bson.Serialization.Conventions { @@ -87,7 +86,7 @@ public void Apply(BsonMemberMap memberMap) private IBsonSerializer Reconfigure(IBsonSerializer serializer) { - if (serializer is IChildSerializerConfigurable childSerializerConfigurable) + if (serializer is IBsonArraySerializer and IChildSerializerConfigurable childSerializerConfigurable) { var childSerializer = childSerializerConfigurable.ChildSerializer; var reconfiguredChildSerializer = Reconfigure(childSerializer); From 6b817305097dc423094bb3e62acdff3c6489c7c6 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:28:32 +0100 Subject: [PATCH 08/19] Corrected calls --- .../EnumRepresentationConvention.cs | 44 +++---------------- .../Serialization/SerializerConfigurator.cs | 18 +++++--- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 80fbd8b87fb..7a7d17055e7 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -66,50 +66,18 @@ public EnumRepresentationConvention(BsonType representation, bool shouldApplyToC /// The member map. public void Apply(BsonMemberMap memberMap) { - var serializer = memberMap.GetSerializer(); + var reconfiguredSerializer = + SerializerConfigurator.ReconfigureSerializer(memberMap.GetSerializer(), + s => s.WithRepresentation(_representation), + s => s.ValueType.IsEnum, _shouldApplyToCollections); - if (memberMap.MemberType.IsEnum && serializer is IRepresentationConfigurable representationConfigurableSerializer) + if (reconfiguredSerializer is not null) { - memberMap.SetSerializer(representationConfigurableSerializer.WithRepresentation(_representation)); - return; - } - - if (_shouldApplyToCollections || IsNullableEnum(memberMap.MemberType)) - { - var reconfiguredSerializer = Reconfigure(serializer); - if (reconfiguredSerializer is not null) - { - memberMap.SetSerializer(reconfiguredSerializer); - } - } - } - - private IBsonSerializer Reconfigure(IBsonSerializer serializer) - { - if (serializer is IBsonArraySerializer and IChildSerializerConfigurable childSerializerConfigurable) - { - var childSerializer = childSerializerConfigurable.ChildSerializer; - var reconfiguredChildSerializer = Reconfigure(childSerializer); - return reconfiguredChildSerializer is null ? null : childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer); - } - - if (serializer.ValueType.IsEnum && serializer is IRepresentationConfigurable representationConfigurable) - { - return representationConfigurable.WithRepresentation(_representation); + memberMap.SetSerializer(reconfiguredSerializer); } - - return null; } // private methods - private bool IsNullableEnum(Type type) - { - return - type.IsGenericType && - type.GetGenericTypeDefinition() == typeof(Nullable<>) && - Nullable.GetUnderlyingType(type)!.IsEnum; - } - private void EnsureRepresentationIsValidForEnums(BsonType representation) { if (representation is 0 or BsonType.String or BsonType.Int32 or BsonType.Int64) diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs index 7e1cfbecbe5..2ff010957b2 100644 --- a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -26,22 +26,26 @@ internal static class SerializerConfigurator /// /// The input serializer to be reconfigured. /// A function that defines how the serializer of type should be reconfigured. + /// TODO + /// TODO /// The input type for the reconfigure method. /// /// The reconfigured serializer, or null if no leaf serializer could be reconfigured. /// - internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure) + internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure, + Func testFunc = null, bool shouldApplyToCollections = true) { switch (serializer) { - case IChildSerializerConfigurable childSerializerConfigurable: + case TSerializer typedSerializer when testFunc?.Invoke(serializer) ?? true: + return reconfigure(typedSerializer); + case IChildSerializerConfigurable childSerializerConfigurable when + shouldApplyToCollections || Nullable.GetUnderlyingType(serializer.ValueType) != null: + { var childSerializer = childSerializerConfigurable.ChildSerializer; - var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure); + var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunc, shouldApplyToCollections); return reconfiguredChildSerializer != null? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null; - - case TSerializer typedSerializer: - return reconfigure(typedSerializer); - + } default: return null; } From db6362329f103d9aef98d210145e501f5c16b175 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:58:33 +0100 Subject: [PATCH 09/19] Corrections --- .../IKeyAndValueSerializerConfigurable.cs | 31 ++++++++++++++ .../Serialization/SerializerConfigurator.cs | 41 +++++++++++-------- ...ictionaryInterfaceImplementerSerializer.cs | 16 ++++++++ ...ictionaryInterfaceImplementerSerializer.cs | 8 ++++ .../EnumRepresentationConventionTests.cs | 18 ++++++++ 5 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 src/MongoDB.Bson/Serialization/IKeyAndValueSerializerConfigurable.cs diff --git a/src/MongoDB.Bson/Serialization/IKeyAndValueSerializerConfigurable.cs b/src/MongoDB.Bson/Serialization/IKeyAndValueSerializerConfigurable.cs new file mode 100644 index 00000000000..4c54f441269 --- /dev/null +++ b/src/MongoDB.Bson/Serialization/IKeyAndValueSerializerConfigurable.cs @@ -0,0 +1,31 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace MongoDB.Bson.Serialization +{ + /// + /// Represents a serializer that has a key and a value serializer that configuration attributes can be forwarded to. + /// + public interface IKeyAndValueSerializerConfigurable : IBsonDictionarySerializer + { + /// + /// Returns a serializer that has been reconfigured with the specified key and value serializers. + /// + /// The key serializer. + /// The value serializer. + /// The reconfigured serializer. + IBsonSerializer WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer); + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs index 2ff010957b2..fb64f5977d7 100644 --- a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -19,31 +19,38 @@ namespace MongoDB.Bson.Serialization { internal static class SerializerConfigurator { - /// - /// Reconfigures a serializer using the specified method. - /// If the serializer implements , - /// the method traverses and applies the reconfiguration to its child serializers recursively until an appropriate leaf serializer is found. - /// - /// The input serializer to be reconfigured. - /// A function that defines how the serializer of type should be reconfigured. - /// TODO - /// TODO - /// The input type for the reconfigure method. - /// - /// The reconfigured serializer, or null if no leaf serializer could be reconfigured. - /// + /// Reconfigures a serializer using the specified method if the result of is true or the function is null. + /// If the serializer implements and either: + /// - is a collection serializer and is true; + /// - or is a serializer; + /// the method traverses and applies the reconfiguration to its child serializers recursively. internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure, - Func testFunc = null, bool shouldApplyToCollections = true) + Func testFunction = null, bool shouldApplyToCollections = true) { switch (serializer) { - case TSerializer typedSerializer when testFunc?.Invoke(serializer) ?? true: + case TSerializer typedSerializer when testFunction?.Invoke(serializer) ?? true: return reconfigure(typedSerializer); case IChildSerializerConfigurable childSerializerConfigurable when - shouldApplyToCollections || Nullable.GetUnderlyingType(serializer.ValueType) != null: + (shouldApplyToCollections && childSerializerConfigurable is IBsonArraySerializer) + || Nullable.GetUnderlyingType(serializer.ValueType) != null: { + if (childSerializerConfigurable is IKeyAndValueSerializerConfigurable keyAndValueSerializerConfigurable) + { + var keySerializer = keyAndValueSerializerConfigurable.KeySerializer; + var valueSerializer = keyAndValueSerializerConfigurable.ValueSerializer; + + var reconfiguredKeySerializer = ReconfigureSerializer(keySerializer, reconfigure, testFunction, + shouldApplyToCollections); + var reconfiguredValueSerializer = ReconfigureSerializer(valueSerializer, reconfigure, testFunction, + shouldApplyToCollections); + + return keyAndValueSerializerConfigurable.WithKeyAndValueSerializers( + reconfiguredKeySerializer ?? keySerializer, reconfiguredValueSerializer ?? valueSerializer); + } + var childSerializer = childSerializerConfigurable.ChildSerializer; - var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunc, shouldApplyToCollections); + var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, shouldApplyToCollections); return reconfiguredChildSerializer != null? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null; } default: diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs index d188eb1c9fe..1bf689c8a65 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs @@ -27,6 +27,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, + IKeyAndValueSerializerConfigurable, IDictionaryRepresentationConfigurable where TDictionary : class, IDictionary, new() { @@ -153,6 +154,13 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati { return WithDictionaryRepresentation(dictionaryRepresentation); } + + IBsonSerializer IKeyAndValueSerializerConfigurable.WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) + { + return valueSerializer.Equals(ValueSerializer) && keySerializer.Equals(KeySerializer) + ? this + : new DictionaryInterfaceImplementerSerializer(DictionaryRepresentation, keySerializer, valueSerializer); + } } /// @@ -164,6 +172,7 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati public class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, + IKeyAndValueSerializerConfigurable, IDictionaryRepresentationConfigurable> where TDictionary : class, IDictionary { @@ -281,6 +290,13 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } + IBsonSerializer IKeyAndValueSerializerConfigurable.WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) + { + return valueSerializer.Equals(ValueSerializer) && keySerializer.Equals(KeySerializer) + ? this + : new DictionaryInterfaceImplementerSerializer(DictionaryRepresentation, (IBsonSerializer)keySerializer, (IBsonSerializer)valueSerializer); + } + /// protected override ICollection> CreateAccumulator() { diff --git a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs index ae2b7479221..1effe8da3ea 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs @@ -28,6 +28,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class ReadOnlyDictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, + IKeyAndValueSerializerConfigurable, IDictionaryRepresentationConfigurable> where TDictionary : class, IReadOnlyDictionary { @@ -122,6 +123,13 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } + IBsonSerializer IKeyAndValueSerializerConfigurable.WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) + { + return valueSerializer.Equals(ValueSerializer) && keySerializer.Equals(KeySerializer) + ? this + : new ReadOnlyDictionaryInterfaceImplementerSerializer(DictionaryRepresentation, (IBsonSerializer)keySerializer, (IBsonSerializer)valueSerializer); + } + /// protected override ICollection> CreateAccumulator() { diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index 827282020a5..3e718e60a07 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -37,6 +37,8 @@ public class C public E[][] ArrayOfArrayEnum { get; set; } public Dictionary DictionaryEnum { get; set; } public Dictionary NestedDictionaryEnum { get; set; } + + public Dictionary DictionaryKeyEnum { get; set; } public int I { get; set; } public int[] ArrayInt { get; set; } } @@ -119,6 +121,22 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary( childSerializer.Representation.Should().Be(representation); } + [Theory] + [InlineData(BsonType.Int32)] + [InlineData(BsonType.Int64)] + [InlineData(BsonType.String)] + public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_key(BsonType representation) + { + var subject = new EnumRepresentationConvention(representation, true); + var memberMap = CreateMemberMap(c => c.DictionaryKeyEnum); + + subject.Apply(memberMap); + + var serializer = (IKeyAndValueSerializerConfigurable)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)serializer.KeySerializer; + childSerializer.Representation.Should().Be(representation); + } + [Theory] [InlineData(BsonType.Int32)] [InlineData(BsonType.Int64)] From 48c71a9e2e2a56c4b45183fd5ad89f6ac1f461dd Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:07:48 +0100 Subject: [PATCH 10/19] Added test --- .../EnumRepresentationConventionTests.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index 3e718e60a07..cdc6dde27d4 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -37,9 +37,9 @@ public class C public E[][] ArrayOfArrayEnum { get; set; } public Dictionary DictionaryEnum { get; set; } public Dictionary NestedDictionaryEnum { get; set; } - public Dictionary DictionaryKeyEnum { get; set; } public int I { get; set; } + public int NI { get; set; } public int[] ArrayInt { get; set; } } @@ -180,6 +180,18 @@ public void Apply_should_do_nothing_when_member_is_not_an_enum() memberMap.GetSerializer().Should().BeSameAs(serializer); } + [Fact] + public void Apply_should_do_nothing_when_member_is_not_an_enum_and_nullable() + { + var subject = new EnumRepresentationConvention(BsonType.String); + var memberMap = CreateMemberMap(c => c.NI); + var serializer = memberMap.GetSerializer(); + + subject.Apply(memberMap); + + memberMap.GetSerializer().Should().BeSameAs(serializer); + } + [Fact] public void Apply_should_do_nothing_when_member_is_not_an_enum_collection() { From faf6095d68cd67126e9dc881c052b0762edb7ed8 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:14:49 +0100 Subject: [PATCH 11/19] Various corrections --- ...ildrenSerializerConfigurableSerializer.cs} | 19 +++++++--- .../Serialization/SerializerConfigurator.cs | 29 +++++++------- ...ictionaryInterfaceImplementerSerializer.cs | 38 +++++++++++++++---- ...ictionaryInterfaceImplementerSerializer.cs | 19 ++++++++-- .../EnumRepresentationConventionTests.cs | 6 +-- 5 files changed, 76 insertions(+), 35 deletions(-) rename src/MongoDB.Bson/Serialization/{IKeyAndValueSerializerConfigurable.cs => IMultipleChildrenSerializerConfigurableSerializer.cs} (57%) diff --git a/src/MongoDB.Bson/Serialization/IKeyAndValueSerializerConfigurable.cs b/src/MongoDB.Bson/Serialization/IMultipleChildrenSerializerConfigurableSerializer.cs similarity index 57% rename from src/MongoDB.Bson/Serialization/IKeyAndValueSerializerConfigurable.cs rename to src/MongoDB.Bson/Serialization/IMultipleChildrenSerializerConfigurableSerializer.cs index 4c54f441269..b73fb2d76c2 100644 --- a/src/MongoDB.Bson/Serialization/IKeyAndValueSerializerConfigurable.cs +++ b/src/MongoDB.Bson/Serialization/IMultipleChildrenSerializerConfigurableSerializer.cs @@ -16,16 +16,23 @@ namespace MongoDB.Bson.Serialization { /// - /// Represents a serializer that has a key and a value serializer that configuration attributes can be forwarded to. + /// Represents a serializer that has multiple children serializers that configuration attributes can be forwarded to. /// - public interface IKeyAndValueSerializerConfigurable : IBsonDictionarySerializer + public interface IMultipleChildrenSerializerConfigurableSerializer { /// - /// Returns a serializer that has been reconfigured with the specified key and value serializers. + /// Gets the children serializers. /// - /// The key serializer. - /// The value serializer. + /// + /// The children serializers. + /// + IBsonSerializer[] ChildrenSerializers { get; } + + /// + /// Returns a serializer that has been reconfigured with the specified children serializers. + /// + /// The children serializers. /// The reconfigured serializer. - IBsonSerializer WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer); + IBsonSerializer WithChildrenSerializers(IBsonSerializer[] childrenSerializers); } } \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs index fb64f5977d7..869ee2715e2 100644 --- a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -14,6 +14,7 @@ */ using System; +using System.Collections.Generic; namespace MongoDB.Bson.Serialization { @@ -21,7 +22,7 @@ internal static class SerializerConfigurator { /// Reconfigures a serializer using the specified method if the result of is true or the function is null. /// If the serializer implements and either: - /// - is a collection serializer and is true; + /// - is true; /// - or is a serializer; /// the method traverses and applies the reconfiguration to its child serializers recursively. internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure, @@ -31,28 +32,28 @@ internal static IBsonSerializer ReconfigureSerializer(IBsonSerializ { case TSerializer typedSerializer when testFunction?.Invoke(serializer) ?? true: return reconfigure(typedSerializer); - case IChildSerializerConfigurable childSerializerConfigurable when - (shouldApplyToCollections && childSerializerConfigurable is IBsonArraySerializer) - || Nullable.GetUnderlyingType(serializer.ValueType) != null: + case IMultipleChildrenSerializerConfigurableSerializer multipleChildrenSerializerConfigurable: { - if (childSerializerConfigurable is IKeyAndValueSerializerConfigurable keyAndValueSerializerConfigurable) - { - var keySerializer = keyAndValueSerializerConfigurable.KeySerializer; - var valueSerializer = keyAndValueSerializerConfigurable.ValueSerializer; + var newSerializers = new List(); - var reconfiguredKeySerializer = ReconfigureSerializer(keySerializer, reconfigure, testFunction, - shouldApplyToCollections); - var reconfiguredValueSerializer = ReconfigureSerializer(valueSerializer, reconfigure, testFunction, + foreach (var childSerializer in multipleChildrenSerializerConfigurable.ChildrenSerializers) + { + var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, shouldApplyToCollections); - return keyAndValueSerializerConfigurable.WithKeyAndValueSerializers( - reconfiguredKeySerializer ?? keySerializer, reconfiguredValueSerializer ?? valueSerializer); + newSerializers.Add(reconfiguredChildSerializer ?? childSerializer); } - + + return multipleChildrenSerializerConfigurable.WithChildrenSerializers(newSerializers.ToArray()); + } + case IChildSerializerConfigurable childSerializerConfigurable when + shouldApplyToCollections || Nullable.GetUnderlyingType(serializer.ValueType) != null: + { var childSerializer = childSerializerConfigurable.ChildSerializer; var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, shouldApplyToCollections); return reconfiguredChildSerializer != null? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null; } + default: return null; } diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs index 1bf689c8a65..cee58b8f9e3 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs @@ -27,7 +27,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IKeyAndValueSerializerConfigurable, + IMultipleChildrenSerializerConfigurableSerializer, IDictionaryRepresentationConfigurable where TDictionary : class, IDictionary, new() { @@ -155,11 +155,22 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer IKeyAndValueSerializerConfigurable.WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) + IBsonSerializer[] IMultipleChildrenSerializerConfigurableSerializer.ChildrenSerializers => [KeySerializer, ValueSerializer]; + + IBsonSerializer IMultipleChildrenSerializerConfigurableSerializer.WithChildrenSerializers(IBsonSerializer[] childrenSerializers) { - return valueSerializer.Equals(ValueSerializer) && keySerializer.Equals(KeySerializer) + if (childrenSerializers.Length != 2) + { + throw new Exception("Wrong number of children serializers passed."); + } + + var newKeySerializer = childrenSerializers[0]; + var newValueSerializer = childrenSerializers[1]; + + return newKeySerializer.Equals(KeySerializer) && newValueSerializer.Equals(ValueSerializer) ? this - : new DictionaryInterfaceImplementerSerializer(DictionaryRepresentation, keySerializer, valueSerializer); + : new DictionaryInterfaceImplementerSerializer(DictionaryRepresentation, newKeySerializer, + newValueSerializer); } } @@ -172,7 +183,7 @@ IBsonSerializer IKeyAndValueSerializerConfigurable.WithKeyAndValueSerializers(IB public class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IKeyAndValueSerializerConfigurable, + IMultipleChildrenSerializerConfigurableSerializer, IDictionaryRepresentationConfigurable> where TDictionary : class, IDictionary { @@ -290,11 +301,22 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer IKeyAndValueSerializerConfigurable.WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) + IBsonSerializer[] IMultipleChildrenSerializerConfigurableSerializer.ChildrenSerializers => [KeySerializer, ValueSerializer]; + + IBsonSerializer IMultipleChildrenSerializerConfigurableSerializer.WithChildrenSerializers(IBsonSerializer[] childrenSerializers) { - return valueSerializer.Equals(ValueSerializer) && keySerializer.Equals(KeySerializer) + if (childrenSerializers.Length != 2) + { + throw new Exception("Wrong number of children serializers passed."); + } + + var newKeySerializer = (IBsonSerializer)childrenSerializers[0]; + var newValueSerializer = (IBsonSerializer)childrenSerializers[1]; + + return newKeySerializer.Equals(KeySerializer) && newValueSerializer.Equals(ValueSerializer) ? this - : new DictionaryInterfaceImplementerSerializer(DictionaryRepresentation, (IBsonSerializer)keySerializer, (IBsonSerializer)valueSerializer); + : new DictionaryInterfaceImplementerSerializer(DictionaryRepresentation, newKeySerializer, + newValueSerializer); } /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs index 1effe8da3ea..436e1de10b4 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs @@ -28,7 +28,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class ReadOnlyDictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IKeyAndValueSerializerConfigurable, + IMultipleChildrenSerializerConfigurableSerializer, IDictionaryRepresentationConfigurable> where TDictionary : class, IReadOnlyDictionary { @@ -123,11 +123,22 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer IKeyAndValueSerializerConfigurable.WithKeyAndValueSerializers(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) + IBsonSerializer[] IMultipleChildrenSerializerConfigurableSerializer.ChildrenSerializers => [KeySerializer, ValueSerializer]; + + IBsonSerializer IMultipleChildrenSerializerConfigurableSerializer.WithChildrenSerializers(IBsonSerializer[] childrenSerializers) { - return valueSerializer.Equals(ValueSerializer) && keySerializer.Equals(KeySerializer) + if (childrenSerializers.Length != 2) + { + throw new Exception("Wrong number of children serializers passed."); + } + + var newKeySerializer = (IBsonSerializer)childrenSerializers[0]; + var newValueSerializer = (IBsonSerializer)childrenSerializers[1]; + + return newKeySerializer.Equals(KeySerializer) && newValueSerializer.Equals(ValueSerializer) ? this - : new ReadOnlyDictionaryInterfaceImplementerSerializer(DictionaryRepresentation, (IBsonSerializer)keySerializer, (IBsonSerializer)valueSerializer); + : new ReadOnlyDictionaryInterfaceImplementerSerializer(DictionaryRepresentation, newKeySerializer, + newValueSerializer); } /// diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index cdc6dde27d4..1454d2ba26a 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -132,8 +132,8 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_ subject.Apply(memberMap); - var serializer = (IKeyAndValueSerializerConfigurable)memberMap.GetSerializer(); - var childSerializer = (EnumSerializer)serializer.KeySerializer; + var serializer = (IMultipleChildrenSerializerConfigurableSerializer)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)serializer.ChildrenSerializers[0]; childSerializer.Representation.Should().Be(representation); } @@ -141,7 +141,7 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_ [InlineData(BsonType.Int32)] [InlineData(BsonType.Int64)] [InlineData(BsonType.String)] - public void Apply_should_configure_serializer_when_member_is_a_nested_enum_dictionary(BsonType representation) + public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_value(BsonType representation) { var subject = new EnumRepresentationConvention(representation, true); var memberMap = CreateMemberMap(c => c.NestedDictionaryEnum); From 9f9402534a36fd8568cc58d2645cc2cedd082e45 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:29:09 +0100 Subject: [PATCH 12/19] Variable name change --- .../BsonDateOnlyOptionsAttribute.cs | 2 +- .../EnumRepresentationConvention.cs | 17 ++++++++-------- .../Serialization/SerializerConfigurator.cs | 12 +++++------ .../EnumRepresentationConventionTests.cs | 20 +++++++++---------- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs index 34ed457ddd8..ac7be35e386 100644 --- a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs +++ b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs @@ -61,7 +61,7 @@ public BsonDateOnlyOptionsAttribute(BsonType representation, DateOnlyDocumentFor /// A reconfigured serializer. protected override IBsonSerializer Apply(IBsonSerializer serializer) { - var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(serializer, (DateOnlySerializer s) => s.WithRepresentation(_representation, _documentFormat)); + var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(serializer, (DateOnlySerializer s) => s.WithRepresentation(_representation, _documentFormat), topLevelOnly: false); return reconfiguredSerializer ?? base.Apply(serializer); } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 7a7d17055e7..ca7dabd4afc 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -24,7 +24,7 @@ public class EnumRepresentationConvention : ConventionBase, IMemberMapConvention { // private fields private readonly BsonType _representation; - private readonly bool _shouldApplyToCollections; + private readonly bool _topLevelOnly; // constructors /// @@ -33,7 +33,7 @@ public class EnumRepresentationConvention : ConventionBase, IMemberMapConvention /// The serialization representation. 0 is used to detect representation /// from the enum itself. public EnumRepresentationConvention(BsonType representation) - :this(representation, false) + :this(representation, true) { } @@ -42,12 +42,12 @@ public EnumRepresentationConvention(BsonType representation) /// /// The serialization representation. 0 is used to detect representation /// from the enum itself. - /// If set to true, the convention will be applied also to collection of enums, recursively. - public EnumRepresentationConvention(BsonType representation, bool shouldApplyToCollections) + /// If set to true, the convention will be applied only to top level enum properties, and not collections of enums, for example. + public EnumRepresentationConvention(BsonType representation, bool topLevelOnly) { EnsureRepresentationIsValidForEnums(representation); _representation = representation; - _shouldApplyToCollections = shouldApplyToCollections; + _topLevelOnly = topLevelOnly; } /// @@ -56,9 +56,10 @@ public EnumRepresentationConvention(BsonType representation, bool shouldApplyToC public BsonType Representation => _representation; /// - /// Gets a boolean indicating if this convention should be also applied to collections of enums. + /// Gets a boolean indicating if this convention should be also applied only to the top level enum properties and not to others, + /// collections of enums for example. True by default. /// - public bool ShouldApplyToCollections => _shouldApplyToCollections; + public bool TopLevelOnly => _topLevelOnly; /// /// Applies a modification to the member map. @@ -69,7 +70,7 @@ public void Apply(BsonMemberMap memberMap) var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(memberMap.GetSerializer(), s => s.WithRepresentation(_representation), - s => s.ValueType.IsEnum, _shouldApplyToCollections); + s => s.ValueType.IsEnum, _topLevelOnly); if (reconfiguredSerializer is not null) { diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs index 869ee2715e2..06b87d17c5d 100644 --- a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -22,24 +22,24 @@ internal static class SerializerConfigurator { /// Reconfigures a serializer using the specified method if the result of is true or the function is null. /// If the serializer implements and either: - /// - is true; + /// - is false; /// - or is a serializer; /// the method traverses and applies the reconfiguration to its child serializers recursively. internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure, - Func testFunction = null, bool shouldApplyToCollections = true) + Func testFunction = null, bool topLevelOnly = false) { switch (serializer) { case TSerializer typedSerializer when testFunction?.Invoke(serializer) ?? true: return reconfigure(typedSerializer); - case IMultipleChildrenSerializerConfigurableSerializer multipleChildrenSerializerConfigurable: + case IMultipleChildrenSerializerConfigurableSerializer multipleChildrenSerializerConfigurable when !topLevelOnly: { var newSerializers = new List(); foreach (var childSerializer in multipleChildrenSerializerConfigurable.ChildrenSerializers) { var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, - shouldApplyToCollections); + false); newSerializers.Add(reconfiguredChildSerializer ?? childSerializer); } @@ -47,10 +47,10 @@ internal static IBsonSerializer ReconfigureSerializer(IBsonSerializ return multipleChildrenSerializerConfigurable.WithChildrenSerializers(newSerializers.ToArray()); } case IChildSerializerConfigurable childSerializerConfigurable when - shouldApplyToCollections || Nullable.GetUnderlyingType(serializer.ValueType) != null: + !topLevelOnly || Nullable.GetUnderlyingType(serializer.ValueType) != null: { var childSerializer = childSerializerConfigurable.ChildSerializer; - var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, shouldApplyToCollections); + var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, topLevelOnly); return reconfiguredChildSerializer != null? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null; } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index 1454d2ba26a..66ab2980093 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -79,7 +79,7 @@ public void Apply_should_configure_serializer_when_member_is_a_nullable_enum(Bso [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_an_enum_collection(BsonType representation) { - var subject = new EnumRepresentationConvention(representation, true); + var subject = new EnumRepresentationConvention(representation, false); var memberMap = CreateMemberMap(c => c.ArrayEnum); subject.Apply(memberMap); @@ -95,7 +95,7 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_collection( [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_a_nested_enum_collection(BsonType representation) { - var subject = new EnumRepresentationConvention(representation, true); + var subject = new EnumRepresentationConvention(representation, false); var memberMap = CreateMemberMap(c => c.ArrayOfArrayEnum); subject.Apply(memberMap); @@ -111,7 +111,7 @@ public void Apply_should_configure_serializer_when_member_is_a_nested_enum_colle [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary(BsonType representation) { - var subject = new EnumRepresentationConvention(representation, true); + var subject = new EnumRepresentationConvention(representation, false); var memberMap = CreateMemberMap(c => c.DictionaryEnum); subject.Apply(memberMap); @@ -127,7 +127,7 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary( [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_key(BsonType representation) { - var subject = new EnumRepresentationConvention(representation, true); + var subject = new EnumRepresentationConvention(representation, false); var memberMap = CreateMemberMap(c => c.DictionaryKeyEnum); subject.Apply(memberMap); @@ -143,7 +143,7 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_ [InlineData(BsonType.String)] public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_value(BsonType representation) { - var subject = new EnumRepresentationConvention(representation, true); + var subject = new EnumRepresentationConvention(representation, false); var memberMap = CreateMemberMap(c => c.NestedDictionaryEnum); subject.Apply(memberMap); @@ -156,9 +156,9 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_ [Theory] [InlineData(BsonType.Int64)] [InlineData(BsonType.String)] - public void Apply_should_do_nothing_when_member_is_an_enum_collection_and_should_apply_to_collection_is_false(BsonType representation) + public void Apply_should_do_nothing_when_member_is_an_enum_collection_and_top_level_only_is_true(BsonType representation) { - var subject = new EnumRepresentationConvention(representation, false); + var subject = new EnumRepresentationConvention(representation, true); var memberMap = CreateMemberMap(c => c.ArrayEnum); subject.Apply(memberMap); @@ -220,12 +220,12 @@ public void constructor_with_representation_should_return_expected_result(BsonTy [ParameterAttributeData] public void constructor_with_representation_and_should_apply_to_collection_should_return_expected_result( [Values((BsonType)0, BsonType.Int32, BsonType.Int64, BsonType.String)] BsonType representation, - [Values(true, false)] bool shouldApplyToCollections) + [Values(true, false)] bool topLevelOnly) { - var subject = new EnumRepresentationConvention(representation, shouldApplyToCollections); + var subject = new EnumRepresentationConvention(representation, topLevelOnly); subject.Representation.Should().Be(representation); - subject.ShouldApplyToCollections.Should().Be(shouldApplyToCollections); + subject.TopLevelOnly.Should().Be(topLevelOnly); } [Theory] From e86f3fa5487a565d4ea5c33761d86d8fce3f6a12 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:36:11 +0100 Subject: [PATCH 13/19] Removed unnecessary --- .../Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs index ac7be35e386..34ed457ddd8 100644 --- a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs +++ b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs @@ -61,7 +61,7 @@ public BsonDateOnlyOptionsAttribute(BsonType representation, DateOnlyDocumentFor /// A reconfigured serializer. protected override IBsonSerializer Apply(IBsonSerializer serializer) { - var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(serializer, (DateOnlySerializer s) => s.WithRepresentation(_representation, _documentFormat), topLevelOnly: false); + var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(serializer, (DateOnlySerializer s) => s.WithRepresentation(_representation, _documentFormat)); return reconfiguredSerializer ?? base.Apply(serializer); } } From 2d1132179e6d45d73af9fa17b75489eeafb2782f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:44:00 +0100 Subject: [PATCH 14/19] Corrected grammar --- ....cs => IMultipleChildSerializerConfigurable.cs} | 14 +++++++------- .../Serialization/SerializerConfigurator.cs | 6 +++--- .../DictionaryInterfaceImplementerSerializer.cs | 12 ++++++------ ...OnlyDictionaryInterfaceImplementerSerializer.cs | 6 +++--- .../EnumRepresentationConventionTests.cs | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) rename src/MongoDB.Bson/Serialization/{IMultipleChildrenSerializerConfigurableSerializer.cs => IMultipleChildSerializerConfigurable.cs} (66%) diff --git a/src/MongoDB.Bson/Serialization/IMultipleChildrenSerializerConfigurableSerializer.cs b/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs similarity index 66% rename from src/MongoDB.Bson/Serialization/IMultipleChildrenSerializerConfigurableSerializer.cs rename to src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs index b73fb2d76c2..187401430fb 100644 --- a/src/MongoDB.Bson/Serialization/IMultipleChildrenSerializerConfigurableSerializer.cs +++ b/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs @@ -16,23 +16,23 @@ namespace MongoDB.Bson.Serialization { /// - /// Represents a serializer that has multiple children serializers that configuration attributes can be forwarded to. + /// Represents a serializer that has multiple child serializers that configuration attributes can be forwarded to. /// - public interface IMultipleChildrenSerializerConfigurableSerializer + public interface IMultipleChildSerializerConfigurable { /// - /// Gets the children serializers. + /// Gets the child serializers. /// /// /// The children serializers. /// - IBsonSerializer[] ChildrenSerializers { get; } + IBsonSerializer[] ChildSerializers { get; } /// - /// Returns a serializer that has been reconfigured with the specified children serializers. + /// Returns a serializer that has been reconfigured with the specified child serializers. /// - /// The children serializers. + /// The child serializers. /// The reconfigured serializer. - IBsonSerializer WithChildrenSerializers(IBsonSerializer[] childrenSerializers); + IBsonSerializer WithChildSerializers(IBsonSerializer[] childrenSerializers); } } \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs index 06b87d17c5d..2c0c9d8711a 100644 --- a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -32,11 +32,11 @@ internal static IBsonSerializer ReconfigureSerializer(IBsonSerializ { case TSerializer typedSerializer when testFunction?.Invoke(serializer) ?? true: return reconfigure(typedSerializer); - case IMultipleChildrenSerializerConfigurableSerializer multipleChildrenSerializerConfigurable when !topLevelOnly: + case IMultipleChildSerializerConfigurable multipleChildrenSerializerConfigurable when !topLevelOnly: { var newSerializers = new List(); - foreach (var childSerializer in multipleChildrenSerializerConfigurable.ChildrenSerializers) + foreach (var childSerializer in multipleChildrenSerializerConfigurable.ChildSerializers) { var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, false); @@ -44,7 +44,7 @@ internal static IBsonSerializer ReconfigureSerializer(IBsonSerializ newSerializers.Add(reconfiguredChildSerializer ?? childSerializer); } - return multipleChildrenSerializerConfigurable.WithChildrenSerializers(newSerializers.ToArray()); + return multipleChildrenSerializerConfigurable.WithChildSerializers(newSerializers.ToArray()); } case IChildSerializerConfigurable childSerializerConfigurable when !topLevelOnly || Nullable.GetUnderlyingType(serializer.ValueType) != null: diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs index cee58b8f9e3..574290b21f4 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs @@ -27,7 +27,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IMultipleChildrenSerializerConfigurableSerializer, + IMultipleChildSerializerConfigurable, IDictionaryRepresentationConfigurable where TDictionary : class, IDictionary, new() { @@ -155,9 +155,9 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer[] IMultipleChildrenSerializerConfigurableSerializer.ChildrenSerializers => [KeySerializer, ValueSerializer]; + IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildrenSerializerConfigurableSerializer.WithChildrenSerializers(IBsonSerializer[] childrenSerializers) + IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childrenSerializers) { if (childrenSerializers.Length != 2) { @@ -183,7 +183,7 @@ IBsonSerializer IMultipleChildrenSerializerConfigurableSerializer.WithChildrenSe public class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IMultipleChildrenSerializerConfigurableSerializer, + IMultipleChildSerializerConfigurable, IDictionaryRepresentationConfigurable> where TDictionary : class, IDictionary { @@ -301,9 +301,9 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer[] IMultipleChildrenSerializerConfigurableSerializer.ChildrenSerializers => [KeySerializer, ValueSerializer]; + IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildrenSerializerConfigurableSerializer.WithChildrenSerializers(IBsonSerializer[] childrenSerializers) + IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childrenSerializers) { if (childrenSerializers.Length != 2) { diff --git a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs index 436e1de10b4..9f7ac8e027d 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs @@ -28,7 +28,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class ReadOnlyDictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IMultipleChildrenSerializerConfigurableSerializer, + IMultipleChildSerializerConfigurable, IDictionaryRepresentationConfigurable> where TDictionary : class, IReadOnlyDictionary { @@ -123,9 +123,9 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer[] IMultipleChildrenSerializerConfigurableSerializer.ChildrenSerializers => [KeySerializer, ValueSerializer]; + IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildrenSerializerConfigurableSerializer.WithChildrenSerializers(IBsonSerializer[] childrenSerializers) + IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childrenSerializers) { if (childrenSerializers.Length != 2) { diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index 66ab2980093..2fc4b783ac2 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -132,8 +132,8 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_ subject.Apply(memberMap); - var serializer = (IMultipleChildrenSerializerConfigurableSerializer)memberMap.GetSerializer(); - var childSerializer = (EnumSerializer)serializer.ChildrenSerializers[0]; + var serializer = (IMultipleChildSerializerConfigurable)memberMap.GetSerializer(); + var childSerializer = (EnumSerializer)serializer.ChildSerializers[0]; childSerializer.Representation.Should().Be(representation); } From 5234bdb4e59de47bdb0111f3917233794ae1fe8d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:45:01 +0100 Subject: [PATCH 15/19] Grammar corrections --- src/MongoDB.Bson/Serialization/SerializerConfigurator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs index 2c0c9d8711a..de07deb9f50 100644 --- a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -32,11 +32,11 @@ internal static IBsonSerializer ReconfigureSerializer(IBsonSerializ { case TSerializer typedSerializer when testFunction?.Invoke(serializer) ?? true: return reconfigure(typedSerializer); - case IMultipleChildSerializerConfigurable multipleChildrenSerializerConfigurable when !topLevelOnly: + case IMultipleChildSerializerConfigurable multipleChildSerializerConfigurable when !topLevelOnly: { var newSerializers = new List(); - foreach (var childSerializer in multipleChildrenSerializerConfigurable.ChildSerializers) + foreach (var childSerializer in multipleChildSerializerConfigurable.ChildSerializers) { var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, false); @@ -44,7 +44,7 @@ internal static IBsonSerializer ReconfigureSerializer(IBsonSerializ newSerializers.Add(reconfiguredChildSerializer ?? childSerializer); } - return multipleChildrenSerializerConfigurable.WithChildSerializers(newSerializers.ToArray()); + return multipleChildSerializerConfigurable.WithChildSerializers(newSerializers.ToArray()); } case IChildSerializerConfigurable childSerializerConfigurable when !topLevelOnly || Nullable.GetUnderlyingType(serializer.ValueType) != null: From 1f0041f3117658a5da14c06362503a96f3d67617 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:56:51 +0100 Subject: [PATCH 16/19] Corrections --- .../IMultipleChildSerializerConfigurable.cs | 6 +++--- .../DictionaryInterfaceImplementerSerializer.cs | 16 ++++++++-------- ...lyDictionaryInterfaceImplementerSerializer.cs | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs b/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs index 187401430fb..efb5a91455c 100644 --- a/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs +++ b/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs @@ -24,15 +24,15 @@ public interface IMultipleChildSerializerConfigurable /// Gets the child serializers. /// /// - /// The children serializers. + /// The child serializers. /// IBsonSerializer[] ChildSerializers { get; } /// /// Returns a serializer that has been reconfigured with the specified child serializers. /// - /// The child serializers. + /// The child serializers. /// The reconfigured serializer. - IBsonSerializer WithChildSerializers(IBsonSerializer[] childrenSerializers); + IBsonSerializer WithChildSerializers(IBsonSerializer[] childSerializers); } } \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs index 574290b21f4..15ec6bfa8c0 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs @@ -157,15 +157,15 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childrenSerializers) + IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) { - if (childrenSerializers.Length != 2) + if (childSerializers.Length != 2) { throw new Exception("Wrong number of children serializers passed."); } - var newKeySerializer = childrenSerializers[0]; - var newValueSerializer = childrenSerializers[1]; + var newKeySerializer = childSerializers[0]; + var newValueSerializer = childSerializers[1]; return newKeySerializer.Equals(KeySerializer) && newValueSerializer.Equals(ValueSerializer) ? this @@ -303,15 +303,15 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childrenSerializers) + IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) { - if (childrenSerializers.Length != 2) + if (childSerializers.Length != 2) { throw new Exception("Wrong number of children serializers passed."); } - var newKeySerializer = (IBsonSerializer)childrenSerializers[0]; - var newValueSerializer = (IBsonSerializer)childrenSerializers[1]; + var newKeySerializer = (IBsonSerializer)childSerializers[0]; + var newValueSerializer = (IBsonSerializer)childSerializers[1]; return newKeySerializer.Equals(KeySerializer) && newValueSerializer.Equals(ValueSerializer) ? this diff --git a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs index 9f7ac8e027d..43407f2c126 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs @@ -125,15 +125,15 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childrenSerializers) + IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) { - if (childrenSerializers.Length != 2) + if (childSerializers.Length != 2) { throw new Exception("Wrong number of children serializers passed."); } - var newKeySerializer = (IBsonSerializer)childrenSerializers[0]; - var newValueSerializer = (IBsonSerializer)childrenSerializers[1]; + var newKeySerializer = (IBsonSerializer)childSerializers[0]; + var newValueSerializer = (IBsonSerializer)childSerializers[1]; return newKeySerializer.Equals(KeySerializer) && newValueSerializer.Equals(ValueSerializer) ? this From 38d35f4f38b683edfeae858bc0312963f0cd4369 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:05:13 +0100 Subject: [PATCH 17/19] Correction --- .../Serializers/DictionaryInterfaceImplementerSerializer.cs | 4 ++-- .../ReadOnlyDictionaryInterfaceImplementerSerializer.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs index 15ec6bfa8c0..93532cba6a4 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs @@ -161,7 +161,7 @@ IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonS { if (childSerializers.Length != 2) { - throw new Exception("Wrong number of children serializers passed."); + throw new Exception("Wrong number of child serializers passed."); } var newKeySerializer = childSerializers[0]; @@ -307,7 +307,7 @@ IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonS { if (childSerializers.Length != 2) { - throw new Exception("Wrong number of children serializers passed."); + throw new Exception("Wrong number of child serializers passed."); } var newKeySerializer = (IBsonSerializer)childSerializers[0]; diff --git a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs index 43407f2c126..ed7ef4ef502 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs @@ -129,7 +129,7 @@ IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonS { if (childSerializers.Length != 2) { - throw new Exception("Wrong number of children serializers passed."); + throw new Exception("Wrong number of child serializers passed."); } var newKeySerializer = (IBsonSerializer)childSerializers[0]; From 0d117d0a72dc9c048ae5dfef98178980255b624e Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:29:04 +0100 Subject: [PATCH 18/19] Simplified reconfigure serializer method and final corrections Co-authored-by: rstam --- .../BsonDateOnlyOptionsAttribute.cs | 6 ++- .../EnumRepresentationConvention.cs | 11 ++++-- ... IMultipleChildSerializersConfigurable.cs} | 2 +- .../Serialization/SerializerConfigurator.cs | 39 +++++++++---------- ...ictionaryInterfaceImplementerSerializer.cs | 12 +++--- ...ictionaryInterfaceImplementerSerializer.cs | 6 +-- .../EnumRepresentationConventionTests.cs | 2 +- .../Serializers/DateOnlySerializerTests.cs | 6 ++- 8 files changed, 45 insertions(+), 39 deletions(-) rename src/MongoDB.Bson/Serialization/{IMultipleChildSerializerConfigurable.cs => IMultipleChildSerializersConfigurable.cs} (95%) diff --git a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs index 34ed457ddd8..351a8fa121f 100644 --- a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs +++ b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs @@ -61,8 +61,10 @@ public BsonDateOnlyOptionsAttribute(BsonType representation, DateOnlyDocumentFor /// A reconfigured serializer. protected override IBsonSerializer Apply(IBsonSerializer serializer) { - var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(serializer, (DateOnlySerializer s) => s.WithRepresentation(_representation, _documentFormat)); - return reconfiguredSerializer ?? base.Apply(serializer); + return SerializerConfigurator.ReconfigureSerializerRecursively(serializer, Reconfigure) ?? base.Apply(serializer); + + IBsonSerializer Reconfigure(IBsonSerializer s) + => s is DateOnlySerializer dos ? dos.WithRepresentation(_representation, _documentFormat) : null; } } #endif diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index ca7dabd4afc..840d7c7191f 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -67,15 +67,18 @@ public EnumRepresentationConvention(BsonType representation, bool topLevelOnly) /// The member map. public void Apply(BsonMemberMap memberMap) { - var reconfiguredSerializer = - SerializerConfigurator.ReconfigureSerializer(memberMap.GetSerializer(), - s => s.WithRepresentation(_representation), - s => s.ValueType.IsEnum, _topLevelOnly); + var serializer = memberMap.GetSerializer(); + var reconfiguredSerializer = _topLevelOnly && Nullable.GetUnderlyingType(serializer.ValueType) == null ? + Reconfigure(serializer) : + SerializerConfigurator.ReconfigureSerializerRecursively(serializer, Reconfigure); if (reconfiguredSerializer is not null) { memberMap.SetSerializer(reconfiguredSerializer); } + + IBsonSerializer Reconfigure(IBsonSerializer s) + => s.ValueType.IsEnum ? (s as IRepresentationConfigurable)?.WithRepresentation(_representation) : null; } // private methods diff --git a/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs b/src/MongoDB.Bson/Serialization/IMultipleChildSerializersConfigurable.cs similarity index 95% rename from src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs rename to src/MongoDB.Bson/Serialization/IMultipleChildSerializersConfigurable.cs index efb5a91455c..4c9a858ba8a 100644 --- a/src/MongoDB.Bson/Serialization/IMultipleChildSerializerConfigurable.cs +++ b/src/MongoDB.Bson/Serialization/IMultipleChildSerializersConfigurable.cs @@ -18,7 +18,7 @@ namespace MongoDB.Bson.Serialization /// /// Represents a serializer that has multiple child serializers that configuration attributes can be forwarded to. /// - public interface IMultipleChildSerializerConfigurable + public interface IMultipleChildSerializersConfigurable { /// /// Gets the child serializers. diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs index de07deb9f50..d02062a1a4e 100644 --- a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -20,42 +20,39 @@ namespace MongoDB.Bson.Serialization { internal static class SerializerConfigurator { - /// Reconfigures a serializer using the specified method if the result of is true or the function is null. - /// If the serializer implements and either: - /// - is false; - /// - or is a serializer; - /// the method traverses and applies the reconfiguration to its child serializers recursively. - internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure, - Func testFunction = null, bool topLevelOnly = false) + // Reconfigures a serializer recursively. + // The reconfigure Func should return null if it does not apply to a given serializer. + internal static IBsonSerializer ReconfigureSerializerRecursively( + IBsonSerializer serializer, + Func reconfigure) { switch (serializer) { - case TSerializer typedSerializer when testFunction?.Invoke(serializer) ?? true: - return reconfigure(typedSerializer); - case IMultipleChildSerializerConfigurable multipleChildSerializerConfigurable when !topLevelOnly: + // check IMultipleChildSerializersConfigurableSerializer first because some serializers implement both interfaces + case IMultipleChildSerializersConfigurable multipleChildSerializerConfigurable: { - var newSerializers = new List(); + var anyChildSerializerWasReconfigured = false; + var reconfiguredChildSerializers = new List(); foreach (var childSerializer in multipleChildSerializerConfigurable.ChildSerializers) { - var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, - false); - - newSerializers.Add(reconfiguredChildSerializer ?? childSerializer); + var reconfiguredChildSerializer = ReconfigureSerializerRecursively(childSerializer, reconfigure); + anyChildSerializerWasReconfigured |= reconfiguredChildSerializer != null; + reconfiguredChildSerializers.Add(reconfiguredChildSerializer ?? childSerializer); } - return multipleChildSerializerConfigurable.WithChildSerializers(newSerializers.ToArray()); + return anyChildSerializerWasReconfigured ? multipleChildSerializerConfigurable.WithChildSerializers(reconfiguredChildSerializers.ToArray()) : null; } - case IChildSerializerConfigurable childSerializerConfigurable when - !topLevelOnly || Nullable.GetUnderlyingType(serializer.ValueType) != null: + + case IChildSerializerConfigurable childSerializerConfigurable: { var childSerializer = childSerializerConfigurable.ChildSerializer; - var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure, testFunction, topLevelOnly); - return reconfiguredChildSerializer != null? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null; + var reconfiguredChildSerializer = ReconfigureSerializerRecursively(childSerializer, reconfigure); + return reconfiguredChildSerializer != null ? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null; } default: - return null; + return reconfigure(serializer); } } } diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs index 93532cba6a4..59e6cb42365 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs @@ -27,7 +27,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IMultipleChildSerializerConfigurable, + IMultipleChildSerializersConfigurable, IDictionaryRepresentationConfigurable where TDictionary : class, IDictionary, new() { @@ -155,9 +155,9 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; + IBsonSerializer[] IMultipleChildSerializersConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) + IBsonSerializer IMultipleChildSerializersConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) { if (childSerializers.Length != 2) { @@ -183,7 +183,7 @@ IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonS public class DictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IMultipleChildSerializerConfigurable, + IMultipleChildSerializersConfigurable, IDictionaryRepresentationConfigurable> where TDictionary : class, IDictionary { @@ -301,9 +301,9 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; + IBsonSerializer[] IMultipleChildSerializersConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) + IBsonSerializer IMultipleChildSerializersConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) { if (childSerializers.Length != 2) { diff --git a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs index ed7ef4ef502..4d522ca580d 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ReadOnlyDictionaryInterfaceImplementerSerializer.cs @@ -28,7 +28,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class ReadOnlyDictionaryInterfaceImplementerSerializer : DictionarySerializerBase, IChildSerializerConfigurable, - IMultipleChildSerializerConfigurable, + IMultipleChildSerializersConfigurable, IDictionaryRepresentationConfigurable> where TDictionary : class, IReadOnlyDictionary { @@ -123,9 +123,9 @@ IBsonSerializer IDictionaryRepresentationConfigurable.WithDictionaryRepresentati return WithDictionaryRepresentation(dictionaryRepresentation); } - IBsonSerializer[] IMultipleChildSerializerConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; + IBsonSerializer[] IMultipleChildSerializersConfigurable.ChildSerializers => [KeySerializer, ValueSerializer]; - IBsonSerializer IMultipleChildSerializerConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) + IBsonSerializer IMultipleChildSerializersConfigurable.WithChildSerializers(IBsonSerializer[] childSerializers) { if (childSerializers.Length != 2) { diff --git a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs index 2fc4b783ac2..bdba3f1d33d 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Conventions/EnumRepresentationConventionTests.cs @@ -132,7 +132,7 @@ public void Apply_should_configure_serializer_when_member_is_an_enum_dictionary_ subject.Apply(memberMap); - var serializer = (IMultipleChildSerializerConfigurable)memberMap.GetSerializer(); + var serializer = (IMultipleChildSerializersConfigurable)memberMap.GetSerializer(); var childSerializer = (EnumSerializer)serializer.ChildSerializers[0]; childSerializer.Representation.Should().Be(representation); } diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs index 76b1159261c..e4c4eb50e91 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs @@ -40,12 +40,13 @@ public void Attribute_should_set_correct_format() DateTimeTicksFormat = dateOnly, YearMonthDayFormat = dateOnly, IgnoredFormat = dateOnly, + NullableYearMonthDayFormat = dateOnly, ArrayYearMonthDayFormat = [dateOnly, dateOnly] }; var json = testObj.ToJson(); const string expected = """ - { "DateTimeTicksFormat" : { "DateTime" : { "$date" : "2024-10-05T00:00:00Z" }, "Ticks" : 638636832000000000 }, "YearMonthDayFormat" : { "Year" : 2024, "Month" : 10, "Day" : 5 }, "IgnoredFormat" : 638636832000000000, "ArrayYearMonthDayFormat" : [{ "Year" : 2024, "Month" : 10, "Day" : 5 }, { "Year" : 2024, "Month" : 10, "Day" : 5 }] } + { "DateTimeTicksFormat" : { "DateTime" : { "$date" : "2024-10-05T00:00:00Z" }, "Ticks" : 638636832000000000 }, "YearMonthDayFormat" : { "Year" : 2024, "Month" : 10, "Day" : 5 }, "IgnoredFormat" : 638636832000000000, "NullableYearMonthDayFormat" : { "Year" : 2024, "Month" : 10, "Day" : 5 }, "ArrayYearMonthDayFormat" : [{ "Year" : 2024, "Month" : 10, "Day" : 5 }, { "Year" : 2024, "Month" : 10, "Day" : 5 }] } """; Assert.Equal(expected, json); } @@ -366,6 +367,9 @@ private class TestClass [BsonDateOnlyOptions(BsonType.Int64, DateOnlyDocumentFormat.YearMonthDay)] public DateOnly IgnoredFormat { get; set; } + [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay)] + public DateOnly? NullableYearMonthDayFormat { get; set; } + [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay)] public DateOnly[] ArrayYearMonthDayFormat { get; set; } } From c2711c3df5fb655053334f8de66980628f8293cc Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 31 Dec 2024 18:59:14 +0100 Subject: [PATCH 19/19] Fixed nullable check --- .../Conventions/EnumRepresentationConvention.cs | 2 +- src/MongoDB.Bson/Serialization/TypeExtensions.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 840d7c7191f..a57fa18ac5e 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -68,7 +68,7 @@ public EnumRepresentationConvention(BsonType representation, bool topLevelOnly) public void Apply(BsonMemberMap memberMap) { var serializer = memberMap.GetSerializer(); - var reconfiguredSerializer = _topLevelOnly && Nullable.GetUnderlyingType(serializer.ValueType) == null ? + var reconfiguredSerializer = _topLevelOnly && !serializer.ValueType.IsNullableEnum() ? Reconfigure(serializer) : SerializerConfigurator.ReconfigureSerializerRecursively(serializer, Reconfigure); diff --git a/src/MongoDB.Bson/Serialization/TypeExtensions.cs b/src/MongoDB.Bson/Serialization/TypeExtensions.cs index 04f533ede95..83e7c48d205 100644 --- a/src/MongoDB.Bson/Serialization/TypeExtensions.cs +++ b/src/MongoDB.Bson/Serialization/TypeExtensions.cs @@ -29,5 +29,17 @@ public static bool IsAnonymousType(this Type type) type.IsGenericType && type.Name.Contains("Anon"); // don't check for more than "Anon" so it works in mono also } + + public static bool IsNullable(this Type type) + { + return type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + public static bool IsNullableEnum(this Type type) + { + return + type.IsNullable() && + Nullable.GetUnderlyingType(type).IsEnum; + } } }