From 29f0f4c337846ecf269b4b99f48e243a4ab1f13c Mon Sep 17 00:00:00 2001 From: Mike-E Date: Thu, 3 Dec 2020 04:10:54 -0500 Subject: [PATCH 1/9] Added support for ImmutableLists --- .../ExtensionModel/DefaultExtensions.cs | 1 + .../Types/ImmutableListExtension.cs | 94 +++++++++++++++++++ .../Issue485Tests.cs | 25 +++++ 3 files changed, 120 insertions(+) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs create mode 100644 test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs diff --git a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs index 94deaf682..964ee2451 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs @@ -59,6 +59,7 @@ public override IEnumerator GetEnumerator() yield return new AllowedMemberValuesExtension(); yield return new MemberFormatExtension(); yield return ImmutableArrayExtension.Default; + yield return ImmutableListExtension.Default; yield return SerializationExtension.Default; yield return NullableStructureAwareExtension.Default; yield return new CustomSerializationExtension(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs new file mode 100644 index 000000000..4fd1be181 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs @@ -0,0 +1,94 @@ +using ExtendedXmlSerializer.ContentModel; +using ExtendedXmlSerializer.ContentModel.Collections; +using ExtendedXmlSerializer.ContentModel.Content; +using ExtendedXmlSerializer.ContentModel.Format; +using ExtendedXmlSerializer.ContentModel.Identification; +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.Core; +using ExtendedXmlSerializer.Core.Specifications; +using ExtendedXmlSerializer.ReflectionModel; +using JetBrains.Annotations; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel.Types +{ + sealed class ImmutableListExtension : ISerializerExtension + { + public static ImmutableListExtension Default { get; } = new ImmutableListExtension(); + + ImmutableListExtension() : this(new IsAssignableGenericSpecification(typeof(ImmutableList<>))) {} + + readonly ISpecification _specification; + + public ImmutableListExtension(ISpecification specification) => _specification = specification; + + public IServiceRepository Get(IServiceRepository parameter) + => parameter.DecorateContentsWith() + .When(_specification) + .Decorate(); + + void ICommand.Execute(IServices parameter) {} + + sealed class GenericTypes : IGenericTypes + { + readonly static TypeInfo Check = typeof(ImmutableList).GetTypeInfo(); + readonly static ImmutableArray Type = typeof(ImmutableList<>).GetTypeInfo() + .Yield() + .ToImmutableArray(); + + readonly IGenericTypes _types; + + public GenericTypes(IGenericTypes types) => _types = types; + + public ImmutableArray Get(IIdentity parameter) + { + var type = _types.Get(parameter); + var result = Equals(type.Only(), Check) ? Type : type; + return result; + } + } + + sealed class ImmutableLists : Collections + { + public ImmutableLists(RuntimeSerializers serializers, Contents contents) : base(serializers, contents) {} + } + + sealed class Contents : ICollectionContents + { + readonly IInnerContentServices _contents; + readonly IEnumerators _enumerators; + + public Contents(IInnerContentServices contents, IEnumerators enumerators) + { + _contents = contents; + _enumerators = enumerators; + } + + public ISerializer Get(CollectionContentInput parameter) + => new Serializer(Readers.Instance.Get(parameter.ItemType)(_contents, parameter.Item), + new EnumerableWriter(_enumerators, parameter.Item).Adapt()); + + sealed class Readers : Generic + { + public static Readers Instance { get; } = new Readers(); + + Readers() : base(typeof(Reader<>)) {} + } + + sealed class Reader : IReader + { + readonly IReader> _reader; + + [UsedImplicitly] + public Reader(IInnerContentServices services, IReader item) + : this(services.CreateContents>(new CollectionInnerContentHandler(item, services))) {} + + Reader(IReader> reader) => _reader = reader; + + public object Get(IFormatReader parameter) => _reader.Get(parameter).ToImmutableList(); + } + } + } +} \ No newline at end of file diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs new file mode 100644 index 000000000..b03764038 --- /dev/null +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -0,0 +1,25 @@ +using ExtendedXmlSerializer.Configuration; +using ExtendedXmlSerializer.Tests.ReportedIssues.Support; +using FluentAssertions; +using System.Collections.Immutable; +using Xunit; + +namespace ExtendedXmlSerializer.Tests.ReportedIssues +{ + public sealed class Issue485Tests + { + [Fact] + public void Verify() + { + var instance = new[] { 1, 2, 3, 4 }.ToImmutableList(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + } +} \ No newline at end of file From 0cfd3716d21c0ef2fe24e505afdcb3dec63b247d Mon Sep 17 00:00:00 2001 From: Mike-E Date: Fri, 4 Dec 2020 00:15:08 -0500 Subject: [PATCH 2/9] Added support for `ImmutableHashSet` --- .../ExtensionModel/DefaultExtensions.cs | 1 + .../Types/ImmutableHashSetExtension.cs | 94 +++++++++++++++++++ .../Types/ImmutableListExtension.cs | 2 +- .../Issue485Tests.cs | 14 +++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs diff --git a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs index 964ee2451..2f6363493 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs @@ -60,6 +60,7 @@ public override IEnumerator GetEnumerator() yield return new MemberFormatExtension(); yield return ImmutableArrayExtension.Default; yield return ImmutableListExtension.Default; + yield return ImmutableHashSetExtension.Default; yield return SerializationExtension.Default; yield return NullableStructureAwareExtension.Default; yield return new CustomSerializationExtension(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs new file mode 100644 index 000000000..3d371825a --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs @@ -0,0 +1,94 @@ +using ExtendedXmlSerializer.ContentModel; +using ExtendedXmlSerializer.ContentModel.Collections; +using ExtendedXmlSerializer.ContentModel.Content; +using ExtendedXmlSerializer.ContentModel.Format; +using ExtendedXmlSerializer.ContentModel.Identification; +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.Core; +using ExtendedXmlSerializer.Core.Specifications; +using ExtendedXmlSerializer.ReflectionModel; +using JetBrains.Annotations; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel.Types +{ + sealed class ImmutableHashSetExtension : ISerializerExtension + { + public static ImmutableHashSetExtension Default { get; } = new ImmutableHashSetExtension(); + + ImmutableHashSetExtension() : this(new IsAssignableGenericSpecification(typeof(ImmutableHashSet<>))) {} + + readonly ISpecification _specification; + + public ImmutableHashSetExtension(ISpecification specification) => _specification = specification; + + public IServiceRepository Get(IServiceRepository parameter) + => parameter.DecorateContentsWith() + .When(_specification) + .Decorate(); + + void ICommand.Execute(IServices parameter) {} + + sealed class GenericTypes : IGenericTypes + { + readonly static TypeInfo Check = typeof(ImmutableHashSet).GetTypeInfo(); + readonly static ImmutableArray Type = typeof(ImmutableHashSet<>).GetTypeInfo() + .Yield() + .ToImmutableArray(); + + readonly IGenericTypes _types; + + public GenericTypes(IGenericTypes types) => _types = types; + + public ImmutableArray Get(IIdentity parameter) + { + var type = _types.Get(parameter); + var result = Equals(type.Only(), Check) ? Type : type; + return result; + } + } + + sealed class ImmutableHashSets : Collections + { + public ImmutableHashSets(RuntimeSerializers serializers, Contents contents) : base(serializers, contents) {} + } + + sealed class Contents : ICollectionContents + { + readonly IInnerContentServices _contents; + readonly IEnumerators _enumerators; + + public Contents(IInnerContentServices contents, IEnumerators enumerators) + { + _contents = contents; + _enumerators = enumerators; + } + + public ISerializer Get(CollectionContentInput parameter) + => new Serializer(Readers.Instance.Get(parameter.ItemType)(_contents, parameter.Item), + new EnumerableWriter(_enumerators, parameter.Item).Adapt()); + + sealed class Readers : Generic + { + public static Readers Instance { get; } = new Readers(); + + Readers() : base(typeof(Reader<>)) {} + } + + sealed class Reader : IReader + { + readonly IReader> _reader; + + [UsedImplicitly] + public Reader(IInnerContentServices services, IReader item) + : this(services.CreateContents>(new CollectionInnerContentHandler(item, services))) {} + + Reader(IReader> reader) => _reader = reader; + + public object Get(IFormatReader parameter) => _reader.Get(parameter).ToImmutableHashSet(); + } + } + } +} diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs index 4fd1be181..79a3f5044 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs @@ -45,7 +45,7 @@ sealed class GenericTypes : IGenericTypes public ImmutableArray Get(IIdentity parameter) { var type = _types.Get(parameter); - var result = Equals(type.Only(), Check) ? Type : type; + var result = type.Only()?.Equals(Check) ?? false ? Type : type; return result; } } diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs index b03764038..ea6e8f0de 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -18,7 +18,21 @@ public void Verify() .EnableParameterizedContentWithPropertyAssignments() .Create() .ForTesting(); + serializer.Assert(instance, @"1234"); + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyHashSet() + { + var instance = new[] { 1, 2, 3, 4 }.ToImmutableHashSet(); + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + serializer.Assert(instance, @"1234"); serializer.Cycle(instance).Should().BeEquivalentTo(instance); } } From 826e27673af4c862256192565bdb8c035e87bb9b Mon Sep 17 00:00:00 2001 From: Mike-E Date: Fri, 4 Dec 2020 00:19:25 -0500 Subject: [PATCH 3/9] Added `ImmutableSortedSet` support. --- .../ExtensionModel/DefaultExtensions.cs | 1 + .../Types/ImmutableHashSetExtension.cs | 2 +- .../Types/ImmutableSortedSetExtension.cs | 96 +++++++++++++++++++ .../Issue485Tests.cs | 14 +++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs diff --git a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs index 2f6363493..a851f24c7 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs @@ -61,6 +61,7 @@ public override IEnumerator GetEnumerator() yield return ImmutableArrayExtension.Default; yield return ImmutableListExtension.Default; yield return ImmutableHashSetExtension.Default; + yield return ImmutableSortedSetExtension.Default; yield return SerializationExtension.Default; yield return NullableStructureAwareExtension.Default; yield return new CustomSerializationExtension(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs index 3d371825a..2dd18902e 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs @@ -45,7 +45,7 @@ sealed class GenericTypes : IGenericTypes public ImmutableArray Get(IIdentity parameter) { var type = _types.Get(parameter); - var result = Equals(type.Only(), Check) ? Type : type; + var result = type.Only()?.Equals(Check) ?? false ? Type : type; return result; } } diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs new file mode 100644 index 000000000..09d7b9043 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs @@ -0,0 +1,96 @@ +using ExtendedXmlSerializer.ContentModel; +using ExtendedXmlSerializer.ContentModel.Collections; +using ExtendedXmlSerializer.ContentModel.Content; +using ExtendedXmlSerializer.ContentModel.Format; +using ExtendedXmlSerializer.ContentModel.Identification; +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.Core; +using ExtendedXmlSerializer.Core.Specifications; +using ExtendedXmlSerializer.ReflectionModel; +using JetBrains.Annotations; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel.Types +{ + sealed class ImmutableSortedSetExtension : ISerializerExtension + { + public static ImmutableSortedSetExtension Default { get; } = new ImmutableSortedSetExtension(); + + ImmutableSortedSetExtension() : this(new IsAssignableGenericSpecification(typeof(ImmutableSortedSet<>))) {} + + readonly ISpecification _specification; + + public ImmutableSortedSetExtension(ISpecification specification) => _specification = specification; + + public IServiceRepository Get(IServiceRepository parameter) + => parameter.DecorateContentsWith() + .When(_specification) + .Decorate(); + + void ICommand.Execute(IServices parameter) {} + + sealed class GenericTypes : IGenericTypes + { + readonly static TypeInfo Check = typeof(ImmutableSortedSet).GetTypeInfo(); + readonly static ImmutableArray Type = typeof(ImmutableSortedSet<>).GetTypeInfo() + .Yield() + .ToImmutableArray(); + + readonly IGenericTypes _types; + + public GenericTypes(IGenericTypes types) => _types = types; + + public ImmutableArray Get(IIdentity parameter) + { + var type = _types.Get(parameter); + var result = type.Only()?.Equals(Check) ?? false ? Type : type; + return result; + } + } + + sealed class ImmutableSortedSets : Collections + { + public ImmutableSortedSets(RuntimeSerializers serializers, Contents contents) + : base(serializers, contents) {} + } + + sealed class Contents : ICollectionContents + { + readonly IInnerContentServices _contents; + readonly IEnumerators _enumerators; + + public Contents(IInnerContentServices contents, IEnumerators enumerators) + { + _contents = contents; + _enumerators = enumerators; + } + + public ISerializer Get(CollectionContentInput parameter) + => new Serializer(Readers.Instance.Get(parameter.ItemType)(_contents, parameter.Item), + new EnumerableWriter(_enumerators, parameter.Item).Adapt()); + + sealed class Readers : Generic + { + public static Readers Instance { get; } = new Readers(); + + Readers() : base(typeof(Reader<>)) {} + } + + sealed class Reader : IReader + { + readonly IReader> _reader; + + [UsedImplicitly] + public Reader(IInnerContentServices services, IReader item) + : this(services.CreateContents>(new CollectionInnerContentHandler(item, services))) {} + + Reader(IReader> reader) => _reader = reader; + + public object Get(IFormatReader parameter) => _reader.Get(parameter).ToImmutableSortedSet(); + } + } + } + +} diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs index ea6e8f0de..883fd6484 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -35,5 +35,19 @@ public void VerifyHashSet() serializer.Assert(instance, @"1234"); serializer.Cycle(instance).Should().BeEquivalentTo(instance); } + + [Fact] + public void VerifySortedSet() + { + var instance = new[] { 1, 2, 3, 4 }.ToImmutableSortedSet(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + serializer.Assert(instance, @"1234"); + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } } } \ No newline at end of file From 15bdb7709a8af6038b602a17ce2c90220c25ef31 Mon Sep 17 00:00:00 2001 From: Mike-E Date: Fri, 4 Dec 2020 01:20:28 -0500 Subject: [PATCH 4/9] Added support for `ImmutableDictionary`. --- .../ExtensionModel/DefaultExtensions.cs | 1 + .../Types/ImmutableDictionariesExtension.cs | 97 +++++++++++++++++++ .../Issue485Tests.cs | 20 ++++ 3 files changed, 118 insertions(+) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs diff --git a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs index a851f24c7..8e24710f7 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs @@ -62,6 +62,7 @@ public override IEnumerator GetEnumerator() yield return ImmutableListExtension.Default; yield return ImmutableHashSetExtension.Default; yield return ImmutableSortedSetExtension.Default; + yield return ImmutableDictionariesExtension.Default; yield return SerializationExtension.Default; yield return NullableStructureAwareExtension.Default; yield return new CustomSerializationExtension(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs new file mode 100644 index 000000000..6661785f4 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs @@ -0,0 +1,97 @@ +using ExtendedXmlSerializer.ContentModel; +using ExtendedXmlSerializer.ContentModel.Content; +using ExtendedXmlSerializer.ContentModel.Format; +using ExtendedXmlSerializer.ContentModel.Identification; +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.Core; +using ExtendedXmlSerializer.Core.Specifications; +using ExtendedXmlSerializer.ReflectionModel; +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel.Types +{ + sealed class ImmutableDictionariesExtension : ISerializerExtension + { + public static ImmutableDictionariesExtension Default { get; } = new ImmutableDictionariesExtension(); + + ImmutableDictionariesExtension() : this(new IsAssignableGenericSpecification(typeof(ImmutableDictionary<,>))) {} + + readonly ISpecification _specification; + + public ImmutableDictionariesExtension(ISpecification specification) => _specification = specification; + + public IServiceRepository Get(IServiceRepository parameter) + => parameter.DecorateContentsWith() + .When(_specification) + .Decorate(); + + void ICommand.Execute(IServices parameter) {} + + sealed class GenericTypes : IGenericTypes + { + readonly static TypeInfo Check = typeof(ImmutableDictionary).GetTypeInfo(); + readonly static ImmutableArray Type = typeof(ImmutableDictionary<,>).GetTypeInfo() + .Yield() + .ToImmutableArray(); + + readonly IGenericTypes _types; + + public GenericTypes(IGenericTypes types) => _types = types; + + public ImmutableArray Get(IIdentity parameter) + { + var type = _types.Get(parameter); + var result = type.Only()?.Equals(Check) ?? false ? Type : type; + return result; + } + } + + sealed class Contents : IContents + { + readonly IContents _contents; + readonly IGeneric _readers; + readonly Type _type; + + [UsedImplicitly] + public Contents(IContents contents) : this(contents, Readers.Instance, typeof(Dictionary<,>)) {} + + public Contents(IContents contents, IGeneric readers, Type type) + { + _contents = contents; + _readers = readers; + _type = type; + } + + public ISerializer Get(TypeInfo parameter) + { + var types = DictionaryPairTypesLocator.Default.Get(parameter).GetValueOrDefault(); + var type = _type.MakeGenericType(types.KeyType, types.ValueType); + var serializer = _contents.Get(type); + var result = new Serializer(_readers.Get(types.KeyType, types.ValueType)(serializer), serializer); + return result; + } + + sealed class Readers : Generic + { + public static Readers Instance { get; } = new Readers(); + + Readers() : base(typeof(Reader<,>)) {} + } + + sealed class Reader : IReader + { + readonly IReader _reader; + + [UsedImplicitly] + public Reader(IReader reader) => _reader = reader; + + public object Get(IFormatReader parameter) + => _reader.Get(parameter).AsValid>().ToImmutableDictionary(); + } + } + } +} \ No newline at end of file diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs index 883fd6484..ff8037603 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -1,6 +1,7 @@ using ExtendedXmlSerializer.Configuration; using ExtendedXmlSerializer.Tests.ReportedIssues.Support; using FluentAssertions; +using System.Collections.Generic; using System.Collections.Immutable; using Xunit; @@ -49,5 +50,24 @@ public void VerifySortedSet() serializer.Assert(instance, @"1234"); serializer.Cycle(instance).Should().BeEquivalentTo(instance); } + + [Fact] + public void VerifyDictionary() + { + var instance = new Dictionary{["Hello"] = "World"}.ToImmutableDictionary(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + + serializer.Assert(instance, @""); + + var dictionary = serializer.Cycle(instance); + dictionary.Should().BeEquivalentTo(instance); + + dictionary["Hello"].Should().Be(instance["Hello"]); + } } } \ No newline at end of file From 43ce1d805d4b0812af0f674cf623cc9177c3ad38 Mon Sep 17 00:00:00 2001 From: Mike-E Date: Fri, 4 Dec 2020 01:37:36 -0500 Subject: [PATCH 5/9] - Added support for `ImmutableSortedDictionary` - Moved immutable type support to extension method. --- .../ExtensionMethodsForExtensionModel.cs | 15 ++- .../ExtensionModel/DefaultExtensions.cs | 4 - .../ImmutableSortedDictionariesExtension.cs | 100 ++++++++++++++++++ .../Issue485Tests.cs | 48 ++++++++- 4 files changed, 157 insertions(+), 10 deletions(-) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs diff --git a/src/ExtendedXmlSerializer/ExtensionMethodsForExtensionModel.cs b/src/ExtendedXmlSerializer/ExtensionMethodsForExtensionModel.cs index 6dbe09bc0..75b6a392e 100644 --- a/src/ExtendedXmlSerializer/ExtensionMethodsForExtensionModel.cs +++ b/src/ExtendedXmlSerializer/ExtensionMethodsForExtensionModel.cs @@ -127,6 +127,19 @@ public static IConfigurationContainer EnableMarkupExtensions(this IConfiguration public static IConfigurationContainer EnableAllConstructors(this IConfigurationContainer @this) => @this.Extend(AllConstructorsExtension.Default); + /// + /// Enables the use of types found in the `System.Collections.Immutable` namespace. + /// + /// The configuration container to configure. + /// The configured configuration container. + /// + public static IConfigurationContainer EnableImmutableTypes(this IConfigurationContainer @this) + => @this.Extend(ImmutableListExtension.Default) + .Extend(ImmutableHashSetExtension.Default) + .Extend(ImmutableSortedSetExtension.Default) + .Extend(ImmutableDictionariesExtension.Default) + .Extend(ImmutableSortedDictionariesExtension.Default); + #region Obsolete /// @@ -217,7 +230,7 @@ public static IConfigurationContainer AllowExistingInstances(this IConfiguration /// /// [Obsolete( - "This method is being deprecated. Please use ConfigurationContainer.WithUnknownContent.Call instead.")] + "This method is being deprecated. Please use ConfigurationContainer.WithUnknownContent.Call instead.")] public static IConfigurationContainer EnableUnknownContentHandling(this IConfigurationContainer @this, Action onMissing) => @this.WithUnknownContent().Call(onMissing); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs index 8e24710f7..94deaf682 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs @@ -59,10 +59,6 @@ public override IEnumerator GetEnumerator() yield return new AllowedMemberValuesExtension(); yield return new MemberFormatExtension(); yield return ImmutableArrayExtension.Default; - yield return ImmutableListExtension.Default; - yield return ImmutableHashSetExtension.Default; - yield return ImmutableSortedSetExtension.Default; - yield return ImmutableDictionariesExtension.Default; yield return SerializationExtension.Default; yield return NullableStructureAwareExtension.Default; yield return new CustomSerializationExtension(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs new file mode 100644 index 000000000..cf7eb5574 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs @@ -0,0 +1,100 @@ +using ExtendedXmlSerializer.ContentModel; +using ExtendedXmlSerializer.ContentModel.Content; +using ExtendedXmlSerializer.ContentModel.Format; +using ExtendedXmlSerializer.ContentModel.Identification; +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.Core; +using ExtendedXmlSerializer.Core.Specifications; +using ExtendedXmlSerializer.ReflectionModel; +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel.Types +{ + sealed class ImmutableSortedDictionariesExtension : ISerializerExtension + { + public static ImmutableSortedDictionariesExtension Default { get; } + = new ImmutableSortedDictionariesExtension(); + + ImmutableSortedDictionariesExtension() + : this(new IsAssignableGenericSpecification(typeof(ImmutableSortedDictionary<,>))) {} + + readonly ISpecification _specification; + + public ImmutableSortedDictionariesExtension(ISpecification specification) + => _specification = specification; + + public IServiceRepository Get(IServiceRepository parameter) + => parameter.DecorateContentsWith() + .When(_specification) + .Decorate(); + + void ICommand.Execute(IServices parameter) {} + + sealed class GenericTypes : IGenericTypes + { + readonly static TypeInfo Check = typeof(ImmutableSortedDictionary).GetTypeInfo(); + readonly static ImmutableArray Type = typeof(ImmutableSortedDictionary<,>).GetTypeInfo() + .Yield() + .ToImmutableArray(); + + readonly IGenericTypes _types; + + public GenericTypes(IGenericTypes types) => _types = types; + + public ImmutableArray Get(IIdentity parameter) + { + var type = _types.Get(parameter); + var result = type.Only()?.Equals(Check) ?? false ? Type : type; + return result; + } + } + + sealed class Contents : IContents + { + readonly IContents _contents; + readonly IGeneric _readers; + readonly Type _type; + + [UsedImplicitly] + public Contents(IContents contents) : this(contents, Readers.Instance, typeof(Dictionary<,>)) {} + + public Contents(IContents contents, IGeneric readers, Type type) + { + _contents = contents; + _readers = readers; + _type = type; + } + + public ISerializer Get(TypeInfo parameter) + { + var types = DictionaryPairTypesLocator.Default.Get(parameter).GetValueOrDefault(); + var type = _type.MakeGenericType(types.KeyType, types.ValueType); + var serializer = _contents.Get(type); + var result = new Serializer(_readers.Get(types.KeyType, types.ValueType)(serializer), serializer); + return result; + } + + sealed class Readers : Generic + { + public static Readers Instance { get; } = new Readers(); + + Readers() : base(typeof(Reader<,>)) {} + } + + sealed class Reader : IReader + { + readonly IReader _reader; + + [UsedImplicitly] + public Reader(IReader reader) => _reader = reader; + + public object Get(IFormatReader parameter) + => _reader.Get(parameter).AsValid>().ToImmutableSortedDictionary(); + } + } + } +} \ No newline at end of file diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs index ff8037603..29fa0ecac 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -16,10 +16,12 @@ public void Verify() var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() + .EnableImmutableTypes() .EnableParameterizedContentWithPropertyAssignments() .Create() .ForTesting(); - serializer.Assert(instance, @"1234"); + serializer.Assert(instance, + @"1234"); serializer.Cycle(instance).Should().BeEquivalentTo(instance); } @@ -30,10 +32,12 @@ public void VerifyHashSet() var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() + .EnableImmutableTypes() .EnableParameterizedContentWithPropertyAssignments() .Create() .ForTesting(); - serializer.Assert(instance, @"1234"); + serializer.Assert(instance, + @"1234"); serializer.Cycle(instance).Should().BeEquivalentTo(instance); } @@ -44,25 +48,59 @@ public void VerifySortedSet() var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() + .EnableImmutableTypes() .EnableParameterizedContentWithPropertyAssignments() .Create() .ForTesting(); - serializer.Assert(instance, @"1234"); + serializer.Assert(instance, + @"1234"); serializer.Cycle(instance).Should().BeEquivalentTo(instance); } [Fact] public void VerifyDictionary() { - var instance = new Dictionary{["Hello"] = "World"}.ToImmutableDictionary(); + var instance = new Dictionary + { + ["Hello"] = "World" + }.ToImmutableDictionary(); var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() + .EnableImmutableTypes() .EnableParameterizedContentWithPropertyAssignments() .Create() .ForTesting(); - serializer.Assert(instance, @""); + serializer.Assert(instance, + @""); + + var dictionary = serializer.Cycle(instance); + dictionary.Should().BeEquivalentTo(instance); + + dictionary["Hello"].Should().Be(instance["Hello"]); + } + + [Fact] + public void VerifySortedDictionary() + { + var instance = new Dictionary + { + ["First"] = "Value1", + ["Hello"] = "World", + ["Second"] = "Value2", + ["Last"] = "Value2", + }.ToImmutableSortedDictionary(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + + serializer.Assert(instance, + @""); var dictionary = serializer.Cycle(instance); dictionary.Should().BeEquivalentTo(instance); From 93f2e12db16159ad9b6dc19bcacdb5b611cce967 Mon Sep 17 00:00:00 2001 From: Mike-E Date: Sat, 5 Dec 2020 09:32:24 -0500 Subject: [PATCH 6/9] Adjusted ConstructorLocator to be an injected dependency rather than a static one. --- .../Configuration/EmitBehaviors.cs | 6 +- .../Members/AllowedAssignedInstanceValues.cs | 8 +- .../Reflection/DefaultTypeDefaults.cs | 13 ++ .../ContentModel/Reflection/TypeDefaults.cs | 17 ++- .../Reflection/TypeMemberDefaults.cs | 13 +- .../ExtensionMethodsForSerialization.cs | 2 +- ...ImplicitlyDefinedDefaultValueAlteration.cs | 6 +- .../AllMembersParameterizedActivators.cs | 11 +- .../Members/ParameterizedActivators.cs | 18 ++- .../Markup/MarkupExtensionPartsEvaluator.cs | 19 ++- .../ExtensionModel/Markup/MarkupExtensions.cs | 11 +- .../Types/ActivationContexts.cs | 27 ++-- .../ExtensionModel/Types/Activators.cs | 7 +- .../Types/TypeModelExtension.cs | 3 +- .../ExtensionModel/Xml/InstanceFormatter.cs | 4 +- .../CollectionAwareConstructorLocator.cs | 21 +++ ...Activators.cs => ConstructedActivators.cs} | 136 ++++++++---------- .../DefaultConstructedActivators.cs | 12 ++ .../ReflectionModel/IsInterface.cs | 12 ++ .../ReflectionModel/ListConstructorLocator.cs | 28 ++++ .../ReflectionModel/Support.cs | 4 +- .../Issue485Tests.cs | 29 ++++ 22 files changed, 269 insertions(+), 138 deletions(-) create mode 100644 src/ExtendedXmlSerializer/ContentModel/Reflection/DefaultTypeDefaults.cs create mode 100644 src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs rename src/ExtendedXmlSerializer/ReflectionModel/{DefaultActivators.cs => ConstructedActivators.cs} (54%) create mode 100644 src/ExtendedXmlSerializer/ReflectionModel/DefaultConstructedActivators.cs create mode 100644 src/ExtendedXmlSerializer/ReflectionModel/IsInterface.cs create mode 100644 src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs diff --git a/src/ExtendedXmlSerializer/Configuration/EmitBehaviors.cs b/src/ExtendedXmlSerializer/Configuration/EmitBehaviors.cs index 584289d53..5b4e6779c 100644 --- a/src/ExtendedXmlSerializer/Configuration/EmitBehaviors.cs +++ b/src/ExtendedXmlSerializer/Configuration/EmitBehaviors.cs @@ -1,4 +1,5 @@ using ExtendedXmlSerializer.ContentModel.Members; +using ExtendedXmlSerializer.ContentModel.Reflection; using ExtendedXmlSerializer.ExtensionModel.Content.Members; using ExtendedXmlSerializer.ExtensionModel.Xml.Classic; using System; @@ -35,14 +36,13 @@ public static class EmitBehaviors /// MyProperty {get; set} = true` and `MyProperty` is `false` upon serialization, then the content is emitted. /// public static IEmitBehavior WhenModified { get; } = - new EmitBehavior(new AddAlteration(AllowedAssignedInstanceValues.Default)); + new EmitBehavior(new AddAlteration(new AllowedAssignedInstanceValues(new TypeMemberDefaults(DefaultTypeDefaults.Default)))); #region Obsolete /// [Obsolete("This is considered deprecated and will be removed in a future release. Use EmitBehaviors.WhenModified instead.")] - public static IEmitBehavior Assigned { get; } = - new EmitBehavior(new AddAlteration(AllowedAssignedInstanceValues.Default)); + public static IEmitBehavior Assigned { get; } = WhenModified; /// [Obsolete("This is considered deprecated and will be removed in a future release. Use EmitBehaviors.WhenAssigned instead.")] diff --git a/src/ExtendedXmlSerializer/ContentModel/Members/AllowedAssignedInstanceValues.cs b/src/ExtendedXmlSerializer/ContentModel/Members/AllowedAssignedInstanceValues.cs index 3efe2778c..c33dd7f76 100644 --- a/src/ExtendedXmlSerializer/ContentModel/Members/AllowedAssignedInstanceValues.cs +++ b/src/ExtendedXmlSerializer/ContentModel/Members/AllowedAssignedInstanceValues.cs @@ -7,16 +7,14 @@ namespace ExtendedXmlSerializer.ContentModel.Members { - class AllowedAssignedInstanceValues : IAllowedMemberValues + sealed class AllowedAssignedInstanceValues : IAllowedMemberValues { readonly ISpecification _specification; readonly ITypeMemberDefaults _defaults; readonly IGeneric> _generic; - public static AllowedAssignedInstanceValues Default { get; } = new AllowedAssignedInstanceValues(); - - AllowedAssignedInstanceValues() - : this(ActivatingTypeSpecification.Default, TypeMemberDefaults.Default, + public AllowedAssignedInstanceValues(ITypeMemberDefaults defaults) + : this(ActivatingTypeSpecification.Default, defaults, new Generic>(typeof(Specification<>))) {} public AllowedAssignedInstanceValues(ISpecification specification, ITypeMemberDefaults defaults, diff --git a/src/ExtendedXmlSerializer/ContentModel/Reflection/DefaultTypeDefaults.cs b/src/ExtendedXmlSerializer/ContentModel/Reflection/DefaultTypeDefaults.cs new file mode 100644 index 000000000..9dbc2ed33 --- /dev/null +++ b/src/ExtendedXmlSerializer/ContentModel/Reflection/DefaultTypeDefaults.cs @@ -0,0 +1,13 @@ +using ExtendedXmlSerializer.Core.Sources; +using ExtendedXmlSerializer.ReflectionModel; +using System.Reflection; + +namespace ExtendedXmlSerializer.ContentModel.Reflection +{ + sealed class DefaultTypeDefaults : DecoratedSource, ITypeDefaults + { + public static DefaultTypeDefaults Default { get; } = new DefaultTypeDefaults(); + + DefaultTypeDefaults() : base(new TypeDefaults(DefaultConstructedActivators.Default)) {} + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeDefaults.cs b/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeDefaults.cs index 8fb207998..ac719c27b 100644 --- a/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeDefaults.cs +++ b/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeDefaults.cs @@ -1,23 +1,22 @@ -using System.Reflection; using ExtendedXmlSerializer.Core.Sources; using ExtendedXmlSerializer.ReflectionModel; +using System.Reflection; namespace ExtendedXmlSerializer.ContentModel.Reflection { sealed class TypeDefaults : ReferenceCacheBase, ITypeDefaults { - public static TypeDefaults Default { get; } = new TypeDefaults(); + public static IParameterizedSource Defaults { get; } + = new ReferenceCache(key => new TypeDefaults(key)); + + /*public static TypeDefaults Default { get; } = new TypeDefaults(); - TypeDefaults() : this(DefaultActivators.Default) {} + TypeDefaults() : this(DefaultActivators.Default) {}*/ readonly IActivators _activators; - public TypeDefaults(IActivators activators) - { - _activators = activators; - } + public TypeDefaults(IActivators activators) => _activators = activators; - protected override object Create(TypeInfo parameter) => _activators.Get(parameter.AsType()) - .Get(); + protected override object Create(TypeInfo parameter) => _activators.Get(parameter.AsType()).Get(); } } \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeMemberDefaults.cs b/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeMemberDefaults.cs index 5ef98e8e7..245eeff84 100644 --- a/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeMemberDefaults.cs +++ b/src/ExtendedXmlSerializer/ContentModel/Reflection/TypeMemberDefaults.cs @@ -1,22 +1,19 @@ -using System; -using System.Reflection; using ExtendedXmlSerializer.ContentModel.Members; using ExtendedXmlSerializer.Core.Sources; +using System; +using System.Reflection; namespace ExtendedXmlSerializer.ContentModel.Reflection { sealed class TypeMemberDefaults : ReferenceCacheBase>, ITypeMemberDefaults { - public static TypeMemberDefaults Default { get; } = new TypeMemberDefaults(); + /*public static TypeMemberDefaults Default { get; } = new TypeMemberDefaults(); - TypeMemberDefaults() : this(TypeDefaults.Default) {} + TypeMemberDefaults() : this(TypeDefaults.Default) {}*/ readonly ITypeDefaults _defaults; - public TypeMemberDefaults(ITypeDefaults defaults) - { - _defaults = defaults; - } + public TypeMemberDefaults(ITypeDefaults defaults) => _defaults = defaults; protected override Func Create(TypeInfo parameter) => new MemberDefaults(_defaults.Get(parameter)).Get; diff --git a/src/ExtendedXmlSerializer/ExtensionMethodsForSerialization.cs b/src/ExtendedXmlSerializer/ExtensionMethodsForSerialization.cs index 3b61eb283..97a745729 100644 --- a/src/ExtendedXmlSerializer/ExtensionMethodsForSerialization.cs +++ b/src/ExtendedXmlSerializer/ExtensionMethodsForSerialization.cs @@ -17,7 +17,7 @@ namespace ExtendedXmlSerializer /// public static class ExtensionMethodsForSerialization { - readonly static Func New = DefaultActivators.Default.New; + readonly static Func New = DefaultConstructedActivators.Default.New; readonly static IXmlWriterFactory WriterFactory = new XmlWriterFactory(CloseSettings.Default.Get(ExtensionModel.Xml.Defaults.WriterSettings)); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/ImplicitlyDefinedDefaultValueAlteration.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/ImplicitlyDefinedDefaultValueAlteration.cs index a835a02bd..436ea6190 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Content/ImplicitlyDefinedDefaultValueAlteration.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/ImplicitlyDefinedDefaultValueAlteration.cs @@ -1,8 +1,8 @@ -using System; -using ExtendedXmlSerializer.ContentModel.Conversion; +using ExtendedXmlSerializer.ContentModel.Conversion; using ExtendedXmlSerializer.ContentModel.Reflection; using ExtendedXmlSerializer.Core.Parsing; using ExtendedXmlSerializer.Core.Sources; +using System; namespace ExtendedXmlSerializer.ExtensionModel.Content { @@ -15,7 +15,7 @@ sealed class ImplicitlyDefinedDefaultValueAlteration : IAlteration public IConverter Get(IConverter parameter) { - var @default = TypeDefaults.Default.Get(parameter.Get()); + var @default = DefaultTypeDefaults.Default.Get(parameter.Get()); var parser = new Parser(parameter.Parse, @default); var result = new Converter(parameter, parser.Get, parameter.Format); return result; diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs index 5ba133aa3..6a8c535aa 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs @@ -1,4 +1,5 @@ using ExtendedXmlSerializer.ContentModel.Members; +using ExtendedXmlSerializer.ContentModel.Reflection; using ExtendedXmlSerializer.Core; using ExtendedXmlSerializer.Core.Sources; using ExtendedXmlSerializer.ExtensionModel.Types; @@ -16,18 +17,26 @@ sealed class AllMembersParameterizedActivators : IActivators readonly IQueriedConstructors _constructors; readonly IMemberAccessors _accessors; readonly ITypeMembers _typeMembers; + readonly ITypeDefaults _defaults; readonly IConstructorMembers _members; // ReSharper disable once TooManyDependencies public AllMembersParameterizedActivators(IActivators activators, IQueriedConstructors constructors, IConstructorMembers members, IMemberAccessors accessors, ITypeMembers typeMembers) + : this(activators, constructors, members, accessors, typeMembers, TypeDefaults.Defaults.Get(activators)) {} + + // ReSharper disable once TooManyDependencies + public AllMembersParameterizedActivators(IActivators activators, IQueriedConstructors constructors, + IConstructorMembers members, IMemberAccessors accessors, + ITypeMembers typeMembers, ITypeDefaults defaults) { _activators = activators; _constructors = constructors; _members = members; _accessors = accessors; _typeMembers = typeMembers; + _defaults = defaults; } public IActivator Get(Type parameter) @@ -46,7 +55,7 @@ ActivationContextActivator Activator(ConstructorInfo constructor, ImmutableArray { var activator = new Source(constructor).ToSelectionDelegate(); var context = new MemberContext(constructor.ReflectedType.GetTypeInfo(), members); - var contexts = new ActivationContexts(_accessors, context, activator); + var contexts = new ActivationContexts(_accessors, context, activator, _defaults); var defaults = constructor.GetParameters() .Where(x => x.IsOptional) .Select(x => Pairs.Create(x.Name, x.DefaultValue)) diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs index 553967bde..df15624cd 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs @@ -1,4 +1,5 @@ using ExtendedXmlSerializer.ContentModel.Members; +using ExtendedXmlSerializer.ContentModel.Reflection; using ExtendedXmlSerializer.Core; using ExtendedXmlSerializer.Core.Sources; using ExtendedXmlSerializer.ExtensionModel.Types; @@ -15,16 +16,23 @@ sealed class ParameterizedActivators : IActivators readonly IActivators _activators; readonly IQueriedConstructors _constructors; readonly IMemberAccessors _accessors; + readonly ITypeDefaults _defaults; readonly IConstructorMembers _members; // ReSharper disable once TooManyDependencies public ParameterizedActivators(IActivators activators, IQueriedConstructors constructors, IConstructorMembers members, IMemberAccessors accessors) + : this(activators, constructors, members, accessors, TypeDefaults.Defaults.Get(activators)) {} + + // ReSharper disable once TooManyDependencies + public ParameterizedActivators(IActivators activators, IQueriedConstructors constructors, + IConstructorMembers members, IMemberAccessors accessors, ITypeDefaults defaults) { - _activators = activators; - _constructors = constructors; - _members = members; - _accessors = accessors; + _activators = activators; + _constructors = constructors; + _members = members; + _accessors = accessors; + _defaults = defaults; } public IActivator Get(Type parameter) @@ -42,7 +50,7 @@ ActivationContextActivator Activator(ConstructorInfo constructor, ImmutableArray { var activator = new Source(constructor).ToSelectionDelegate(); var context = new MemberContext(constructor.ReflectedType.GetTypeInfo(), members); - var contexts = new ActivationContexts(_accessors, context, activator); + var contexts = new ActivationContexts(_accessors, context, activator, _defaults); var defaults = constructor.GetParameters() .Where(x => x.IsOptional) .Select(x => Pairs.Create(x.Name, x.DefaultValue)) diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensionPartsEvaluator.cs b/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensionPartsEvaluator.cs index 31c0f494b..cdb0a2944 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensionPartsEvaluator.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensionPartsEvaluator.cs @@ -2,6 +2,7 @@ using ExtendedXmlSerializer.ContentModel.Identification; using ExtendedXmlSerializer.ContentModel.Members; using ExtendedXmlSerializer.ContentModel.Properties; +using ExtendedXmlSerializer.ContentModel.Reflection; using ExtendedXmlSerializer.Core; using ExtendedXmlSerializer.Core.Parsing; using ExtendedXmlSerializer.Core.Sources; @@ -33,17 +34,21 @@ sealed class MarkupExtensionPartsEvaluator readonly IConstructors _constructors; readonly System.IServiceProvider _provider; readonly IFormatter _formatter; + readonly ITypeDefaults _defaults; readonly object[] _services; - public MarkupExtensionPartsEvaluator(IParser parser, IEvaluator evaluator, ITypeMembers members, + public MarkupExtensionPartsEvaluator(IActivators activators, + IParser parser, IEvaluator evaluator, ITypeMembers members, IMemberAccessors accessors, IConstructors constructors, - System.IServiceProvider provider, params object[] services) + System.IServiceProvider provider, + params object[] services) : this(parser, evaluator, members, accessors, constructors, provider, TypePartsFormatter.Default, - services) {} + TypeDefaults.Defaults.Get(activators), services) {} public MarkupExtensionPartsEvaluator(IParser parser, IEvaluator evaluator, ITypeMembers members, IMemberAccessors accessors, IConstructors constructors, System.IServiceProvider provider, IFormatter formatter, + ITypeDefaults defaults, params object[] services) { _parser = parser; @@ -53,6 +58,7 @@ public MarkupExtensionPartsEvaluator(IParser parser, IEvaluator eval _constructors = constructors; _provider = provider; _formatter = formatter; + _defaults = defaults; _services = services; } @@ -81,9 +87,10 @@ protected override object Create(MarkupExtensionParts parameter) .ToArray(); var activator = new ConstructedActivator(constructor, arguments); var context = new MemberContext(constructor.ReflectedType.GetTypeInfo(), members); - var extension = new ActivationContexts(_accessors, context, activator).Get(dictionary) - .Get() - .AsValid(); + var extension = new ActivationContexts(_accessors, context, activator, _defaults) + .Get(dictionary) + .Get() + .AsValid(); var result = extension.ProvideValue(new Provider(_provider, _services.Appending(parameter) .ToArray())); return result; diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensions.cs b/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensions.cs index a29f0fd5e..6538bdd33 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensions.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Markup/MarkupExtensions.cs @@ -10,6 +10,7 @@ sealed class MarkupExtensions : ReferenceCacheBase, IMarkupExtensions { + readonly IActivators _activators; readonly IEvaluator _evaluator; readonly ITypeMembers _members; readonly IMemberAccessors _accessors; @@ -17,9 +18,11 @@ sealed class MarkupExtensions readonly System.IServiceProvider _provider; // ReSharper disable once TooManyDependencies - public MarkupExtensions(IEvaluator evaluator, ITypeMembers members, IMemberAccessors accessors, - IConstructors constructors, System.IServiceProvider provider) + public MarkupExtensions(IActivators activators, IEvaluator evaluator, ITypeMembers members, + IMemberAccessors accessors, IConstructors constructors, + System.IServiceProvider provider) { + _activators = activators; _evaluator = evaluator; _members = members; _accessors = accessors; @@ -28,7 +31,7 @@ public MarkupExtensions(IEvaluator evaluator, ITypeMembers members, IMemberAcces } protected override IMarkupExtensionPartsEvaluator Create(IFormatReader parameter) - => new MarkupExtensionPartsEvaluator(parameter, _evaluator, _members, _accessors, _constructors, _provider, - parameter); + => new MarkupExtensionPartsEvaluator(_activators, parameter, _evaluator, _members, _accessors, + _constructors, _provider, parameter); } } \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ActivationContexts.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ActivationContexts.cs index 522998604..3a094c714 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ActivationContexts.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ActivationContexts.cs @@ -15,27 +15,39 @@ sealed class ActivationContexts : IActivationContexts readonly MemberContext _members; readonly ITableSource _table; readonly Func, IActivator> _activator; + readonly ITypeDefaults _defaults; - public ActivationContexts(IMemberAccessors accessors, MemberContext members, IActivator activator) - : this(accessors, members, activator.Accept) {} + // ReSharper disable once TooManyDependencies + public ActivationContexts(IActivators activators, IMemberAccessors accessors, MemberContext members, + IActivator activator) + : this(accessors, members, activator, TypeDefaults.Defaults.Get(activators)) {} + + // ReSharper disable once TooManyDependencies + public ActivationContexts(IMemberAccessors accessors, MemberContext members, IActivator activator, + ITypeDefaults defaults) + : this(accessors, members, activator.Accept, defaults) {} + // ReSharper disable once TooManyDependencies public ActivationContexts(IMemberAccessors accessors, MemberContext members, - Func, IActivator> activator) + Func, IActivator> activator, ITypeDefaults defaults) : this(accessors, members, new TableSource(members.Members .ToDictionary(x => x.Name, StringComparer.InvariantCultureIgnoreCase)), - activator) {} + activator, defaults) {} // ReSharper disable once TooManyDependencies public ActivationContexts(IMemberAccessors accessors, MemberContext members, ITableSource table, - Func, IActivator> activator) + Func, IActivator> activator, + ITypeDefaults defaults + ) { _accessors = accessors; _members = members; _table = table; _activator = activator; + _defaults = defaults; } public IActivationContext Get(IDictionary parameter) @@ -46,7 +58,7 @@ public IActivationContext Get(IDictionary parameter) source), new AddItemsCommand(list)); var alteration = new ConfiguringAlteration(command); - var activator = new AlteringActivator(alteration, _activator(new Store(source, _table).Get)); + var activator = new AlteringActivator(alteration, _activator(new Store(source, _table, _defaults).Get)); var result = new ActivationContext(_members.ReflectedType, source, activator.Singleton().Get, list); return result; } @@ -57,9 +69,6 @@ sealed class Store : IParameterizedSource readonly IParameterizedSource _members; readonly ITypeDefaults _defaults; - public Store(ITableSource store, IParameterizedSource members) - : this(store, members, TypeDefaults.Default) {} - public Store(ITableSource store, IParameterizedSource members, ITypeDefaults defaults) { diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/Activators.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/Activators.cs index c061da8f1..31a174722 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/Activators.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/Activators.cs @@ -1,15 +1,14 @@ -using System; using ExtendedXmlSerializer.Core; using ExtendedXmlSerializer.Core.Sources; using ExtendedXmlSerializer.ReflectionModel; +using System; namespace ExtendedXmlSerializer.ExtensionModel.Types { sealed class Activators : ReferenceCacheBase, IActivators { - public static Activators Default { get; } = new Activators(); - - Activators() : this(ActivatingTypeSpecification.Default, DefaultActivators.Default, SingletonLocator.Default) {} + public Activators(IActivators activators) + : this(ActivatingTypeSpecification.Default, activators, SingletonLocator.Default) {} readonly IActivatingTypeSpecification _specification; readonly IActivators _activators; diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/TypeModelExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/TypeModelExtension.cs index 590c9a6f3..c0c8368b6 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/TypeModelExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/TypeModelExtension.cs @@ -20,9 +20,10 @@ public sealed class TypeModelExtension : ISerializerExtension public IServiceRepository Get(IServiceRepository parameter) => parameter.Register() .Register() - .Register() + .Register() .Register() .Register() + .Decorate() .Register() .Register() .RegisterInstance(DictionaryEnumerators.Default) diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Xml/InstanceFormatter.cs b/src/ExtendedXmlSerializer/ExtensionModel/Xml/InstanceFormatter.cs index fd092fcbd..b237d44eb 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Xml/InstanceFormatter.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Xml/InstanceFormatter.cs @@ -1,12 +1,12 @@ +using ExtendedXmlSerializer.ReflectionModel; using System; using System.IO; -using ExtendedXmlSerializer.ReflectionModel; namespace ExtendedXmlSerializer.ExtensionModel.Xml { class InstanceFormatter : IInstanceFormatter { - readonly static Func Stream = DefaultActivators.Default.New; + readonly static Func Stream = DefaultConstructedActivators.Default.New; readonly IExtendedXmlSerializer _serializer; readonly IXmlWriterFactory _factory; diff --git a/src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs b/src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs new file mode 100644 index 000000000..8b4515700 --- /dev/null +++ b/src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs @@ -0,0 +1,21 @@ +using ExtendedXmlSerializer.Core.Specifications; +using System.Collections.Generic; +using System.Reflection; + +namespace ExtendedXmlSerializer.ReflectionModel +{ + sealed class CollectionAwareConstructorLocator : ListConstructorLocator, IConstructorLocator + { + readonly static ISpecification Specification = IsInterface.Default.And(IsCollectionType.Instance); + + public CollectionAwareConstructorLocator(IConstructorLocator previous) : base(Specification, previous) {} + + sealed class IsCollectionType : AnySpecification + { + public static IsCollectionType Instance { get; } = new IsCollectionType(); + + IsCollectionType() : base(IsCollectionTypeSpecification.Default, + new IsAssignableGenericSpecification(typeof(IReadOnlyCollection<>))) {} + } + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ReflectionModel/DefaultActivators.cs b/src/ExtendedXmlSerializer/ReflectionModel/ConstructedActivators.cs similarity index 54% rename from src/ExtendedXmlSerializer/ReflectionModel/DefaultActivators.cs rename to src/ExtendedXmlSerializer/ReflectionModel/ConstructedActivators.cs index 8a8447bba..b1970a353 100644 --- a/src/ExtendedXmlSerializer/ReflectionModel/DefaultActivators.cs +++ b/src/ExtendedXmlSerializer/ReflectionModel/ConstructedActivators.cs @@ -1,76 +1,62 @@ -using ExtendedXmlSerializer.Core.Sources; -using ExtendedXmlSerializer.Core.Specifications; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace ExtendedXmlSerializer.ReflectionModel -{ - sealed class DefaultActivators : ReferenceCacheBase, IActivators - { - readonly static Func Selector = DefaultParameters.Instance.Get; - - public static DefaultActivators Default { get; } = new DefaultActivators(); - - DefaultActivators() : this(ConstructorLocator.Default) {} - - readonly IConstructorLocator _locator; - - public DefaultActivators(IConstructorLocator locator) => _locator = locator; - - protected override IActivator Create(Type parameter) - { - var typeInfo = parameter.GetTypeInfo(); - var expression = parameter == typeof(string) - ? (Expression)Expression.Default(parameter) - : typeInfo.IsValueType - ? Expression.New(parameter) - : Reference(parameter, typeInfo); - var convert = Expression.Convert(expression, typeof(object)); - var lambda = Expression.Lambda>(convert); - var result = new Activator(lambda.Compile()); - return result; - } - - NewExpression Reference(Type parameter, TypeInfo typeInfo) - { - var accounted = typeInfo.IsInterface && IsCollectionType.Instance.IsSatisfiedBy(parameter) - ? typeof(List<>).MakeGenericType(CollectionItemTypeLocator.Default.Get(typeInfo)) - : typeInfo; - var constructor = _locator.Get(accounted) ?? _locator.Get(typeInfo); - var parameters = constructor.GetParameters(); - var result = parameters.Length > 0 - ? Expression.New(constructor, parameters.Select(Selector)) - : Expression.New(accounted); - return result; - } - - sealed class DefaultParameters : IParameterizedSource - { - readonly static IEnumerable Initializers = Enumerable.Empty(); - - public static DefaultParameters Instance { get; } = new DefaultParameters(); - - DefaultParameters() {} - - public Expression Get(ParameterInfo parameter) - => parameter.IsDefined(typeof(ParamArrayAttribute)) - ? (Expression)Expression.NewArrayInit( - parameter.ParameterType.GetElementType() ?? - throw new - InvalidOperationException("Element Type not found."), - Initializers) - : Expression.Default(parameter.ParameterType); - } - - sealed class IsCollectionType : AnySpecification - { - public static IsCollectionType Instance { get; } = new IsCollectionType(); - - IsCollectionType() : base(IsCollectionTypeSpecification.Default, - new IsAssignableGenericSpecification(typeof(IReadOnlyCollection<>))) {} - } - } +using ExtendedXmlSerializer.Core.Sources; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace ExtendedXmlSerializer.ReflectionModel +{ + sealed class ConstructedActivators : ReferenceCacheBase, IActivators + { + readonly static Func Selector = DefaultParameters.Instance.Get; + + /*public static DefaultActivators Default { get; } = new DefaultActivators(); + + DefaultActivators() : this(ConstructorLocator.Default) {}*/ + + readonly IConstructorLocator _locator; + + public ConstructedActivators(IConstructorLocator locator) => _locator = locator; + + protected override IActivator Create(Type parameter) + { + var expression = parameter == typeof(string) + ? (Expression)Expression.Default(parameter) + : parameter.IsValueType + ? Expression.New(parameter) + : Reference(parameter); + var convert = Expression.Convert(expression, typeof(object)); + var lambda = Expression.Lambda>(convert); + var result = new Activator(lambda.Compile()); + return result; + } + + NewExpression Reference(Type parameter) + { + var constructor = _locator.Get(parameter); + var parameters = constructor.GetParameters(); + var result = parameters.Length > 0 + ? Expression.New(constructor, parameters.Select(Selector)) + : Expression.New(constructor); + return result; + } + + sealed class DefaultParameters : IParameterizedSource + { + readonly static IEnumerable Initializers = Enumerable.Empty(); + + public static DefaultParameters Instance { get; } = new DefaultParameters(); + + DefaultParameters() {} + + public Expression Get(ParameterInfo parameter) + => parameter.IsDefined(typeof(ParamArrayAttribute)) + ? (Expression)Expression.NewArrayInit(parameter.ParameterType.GetElementType() ?? + throw new + InvalidOperationException("Element Type not found."), + Initializers) + : Expression.Default(parameter.ParameterType); + } + } } \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ReflectionModel/DefaultConstructedActivators.cs b/src/ExtendedXmlSerializer/ReflectionModel/DefaultConstructedActivators.cs new file mode 100644 index 000000000..4f6498463 --- /dev/null +++ b/src/ExtendedXmlSerializer/ReflectionModel/DefaultConstructedActivators.cs @@ -0,0 +1,12 @@ +using ExtendedXmlSerializer.Core.Sources; +using System; + +namespace ExtendedXmlSerializer.ReflectionModel +{ + sealed class DefaultConstructedActivators : DecoratedSource, IActivators + { + public static DefaultConstructedActivators Default { get; } = new DefaultConstructedActivators(); + + DefaultConstructedActivators() : base(new ConstructedActivators(ConstructorLocator.Default)) {} + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ReflectionModel/IsInterface.cs b/src/ExtendedXmlSerializer/ReflectionModel/IsInterface.cs new file mode 100644 index 000000000..26069d3c7 --- /dev/null +++ b/src/ExtendedXmlSerializer/ReflectionModel/IsInterface.cs @@ -0,0 +1,12 @@ +using ExtendedXmlSerializer.Core.Specifications; +using System.Reflection; + +namespace ExtendedXmlSerializer.ReflectionModel +{ + sealed class IsInterface : DelegatedSpecification + { + public static IsInterface Default { get; } = new IsInterface(); + + IsInterface() : base(x => x.IsInterface) {} + } +} diff --git a/src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs b/src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs new file mode 100644 index 000000000..49bd280cc --- /dev/null +++ b/src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs @@ -0,0 +1,28 @@ +using ExtendedXmlSerializer.Core.Sources; +using ExtendedXmlSerializer.Core.Specifications; +using System.Collections.Generic; +using System.Reflection; + +namespace ExtendedXmlSerializer.ReflectionModel +{ + class ListConstructorLocator : IParameterizedSource + { + readonly ISpecification _specification; + readonly IConstructorLocator _previous; + + public ListConstructorLocator(ISpecification specification, IConstructorLocator previous) + { + _specification = specification; + _previous = previous; + } + + public ConstructorInfo Get(TypeInfo parameter) + { + var accounted = _specification.IsSatisfiedBy(parameter) + ? typeof(List<>).MakeGenericType(CollectionItemTypeLocator.Default.Get(parameter)) + : parameter; + var result = _previous.Get(accounted); + return result; + } + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ReflectionModel/Support.cs b/src/ExtendedXmlSerializer/ReflectionModel/Support.cs index e364a0222..8ca6494a4 100644 --- a/src/ExtendedXmlSerializer/ReflectionModel/Support.cs +++ b/src/ExtendedXmlSerializer/ReflectionModel/Support.cs @@ -10,8 +10,8 @@ static class Support public static TypeInfo Metadata { get; } = typeof(T).GetTypeInfo(); - public static Func New { get; } = DefaultActivators.Default.New; + public static Func New { get; } = DefaultConstructedActivators.Default.New; - public static Func NewOrSingleton { get; } = Activators.Default.New; + public static Func NewOrSingleton { get; } = new Activators(DefaultConstructedActivators.Default).New; } } \ No newline at end of file diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs index 29fa0ecac..2efcccac6 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -1,9 +1,11 @@ using ExtendedXmlSerializer.Configuration; using ExtendedXmlSerializer.Tests.ReportedIssues.Support; using FluentAssertions; +using System; using System.Collections.Generic; using System.Collections.Immutable; using Xunit; +// ReSharper disable All namespace ExtendedXmlSerializer.Tests.ReportedIssues { @@ -25,6 +27,33 @@ public void Verify() serializer.Cycle(instance).Should().BeEquivalentTo(instance); } + /*[Fact] + public void VerifyEmpty() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create() + .ForTesting(); + var instance = new SubjectList("Hello World!", ImmutableList.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + }*/ + + class SubjectList + { + public SubjectList(string name, ImmutableList list) + { + Name = name; + List = list; + } + + public string Name { get; } + + public ImmutableList List { get; } + } + [Fact] public void VerifyHashSet() { From f981311c3608d33d9f3c5f45a542c273e29e20e6 Mon Sep 17 00:00:00 2001 From: Mike-E Date: Sat, 5 Dec 2020 10:17:02 -0500 Subject: [PATCH 7/9] Added support for empty immutable objects when parameterized content is enabled. --- .../Members/ParameterizedActivators.cs | 15 +- .../ExtensionModel/Types/IActivationAware.cs | 9 + .../Types/ImmutableDictionariesExtension.cs | 17 +- .../Types/ImmutableHashSetExtension.cs | 18 +- .../Types/ImmutableListExtension.cs | 18 +- .../ImmutableSortedDictionariesExtension.cs | 17 +- .../Types/ImmutableSortedSetExtension.cs | 18 +- .../DictionaryConstructorLocator.cs | 42 +++++ .../ReflectionModel/ListConstructorLocator.cs | 9 +- .../Issue485Tests.cs | 162 +++++++++++++++--- 10 files changed, 286 insertions(+), 39 deletions(-) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Types/IActivationAware.cs create mode 100644 src/ExtendedXmlSerializer/ReflectionModel/DictionaryConstructorLocator.cs diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs index df15624cd..0156cd82c 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs @@ -28,11 +28,11 @@ public ParameterizedActivators(IActivators activators, IQueriedConstructors cons public ParameterizedActivators(IActivators activators, IQueriedConstructors constructors, IConstructorMembers members, IMemberAccessors accessors, ITypeDefaults defaults) { - _activators = activators; - _constructors = constructors; - _members = members; - _accessors = accessors; - _defaults = defaults; + _activators = activators; + _constructors = constructors; + _members = members; + _accessors = accessors; + _defaults = defaults; } public IActivator Get(Type parameter) @@ -69,7 +69,10 @@ public IActivator Get(Func parameter) { var arguments = new Enumerable(_constructor.GetParameters() .Select(x => x.Name) - .Select(parameter)); + .Select(parameter) + .Select(x => x is IActivationAware aware + ? aware.Get() + : x)); var result = new ConstructedActivator(_constructor, arguments); return result; } diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/IActivationAware.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/IActivationAware.cs new file mode 100644 index 000000000..0cabf7c00 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/IActivationAware.cs @@ -0,0 +1,9 @@ +using ExtendedXmlSerializer.Core.Sources; + +namespace ExtendedXmlSerializer.ExtensionModel.Types +{ + /// + /// Convenience interface for activating components. + /// + public interface IActivationAware : ISource {} +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs index 6661785f4..8ad070d42 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableDictionariesExtension.cs @@ -27,10 +27,25 @@ sealed class ImmutableDictionariesExtension : ISerializerExtension public IServiceRepository Get(IServiceRepository parameter) => parameter.DecorateContentsWith() .When(_specification) - .Decorate(); + .Decorate() + .Decorate(Register); + + IConstructorLocator Register(IServiceProvider arg1, IConstructorLocator previous) + => new ConstructorLocator(_specification, previous); void ICommand.Execute(IServices parameter) {} + sealed class ConstructorLocator : DictionaryConstructorLocator, IConstructorLocator + { + public ConstructorLocator(ISpecification specification, IConstructorLocator previous) + : base(specification, previous, typeof(Adapter<,>)) {} + } + + sealed class Adapter : Dictionary, IActivationAware + { + public object Get() => this.ToImmutableDictionary(); + } + sealed class GenericTypes : IGenericTypes { readonly static TypeInfo Check = typeof(ImmutableDictionary).GetTypeInfo(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs index 2dd18902e..15d322881 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableHashSetExtension.cs @@ -8,6 +8,7 @@ using ExtendedXmlSerializer.Core.Specifications; using ExtendedXmlSerializer.ReflectionModel; using JetBrains.Annotations; +using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Reflection; @@ -27,10 +28,25 @@ sealed class ImmutableHashSetExtension : ISerializerExtension public IServiceRepository Get(IServiceRepository parameter) => parameter.DecorateContentsWith() .When(_specification) - .Decorate(); + .Decorate() + .Decorate(Register); + + IConstructorLocator Register(IServiceProvider arg1, IConstructorLocator previous) + => new ConstructorLocator(_specification, previous); void ICommand.Execute(IServices parameter) {} + sealed class ConstructorLocator : ListConstructorLocator, IConstructorLocator + { + public ConstructorLocator(ISpecification specification, IConstructorLocator previous) + : base(specification, previous, typeof(Adapter<>)) {} + } + + sealed class Adapter : List, IActivationAware + { + public object Get() => this.ToImmutableHashSet(); + } + sealed class GenericTypes : IGenericTypes { readonly static TypeInfo Check = typeof(ImmutableHashSet).GetTypeInfo(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs index 79a3f5044..8c5252272 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableListExtension.cs @@ -8,6 +8,7 @@ using ExtendedXmlSerializer.Core.Specifications; using ExtendedXmlSerializer.ReflectionModel; using JetBrains.Annotations; +using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Reflection; @@ -27,10 +28,25 @@ sealed class ImmutableListExtension : ISerializerExtension public IServiceRepository Get(IServiceRepository parameter) => parameter.DecorateContentsWith() .When(_specification) - .Decorate(); + .Decorate() + .Decorate(Register); + + IConstructorLocator Register(IServiceProvider arg1, IConstructorLocator previous) + => new ConstructorLocator(_specification, previous); void ICommand.Execute(IServices parameter) {} + sealed class ConstructorLocator : ListConstructorLocator, IConstructorLocator + { + public ConstructorLocator(ISpecification specification, IConstructorLocator previous) + : base(specification, previous, typeof(Adapter<>)) {} + } + + sealed class Adapter : List, IActivationAware + { + public object Get() => this.ToImmutableList(); + } + sealed class GenericTypes : IGenericTypes { readonly static TypeInfo Check = typeof(ImmutableList).GetTypeInfo(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs index cf7eb5574..b91d8c5f1 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedDictionariesExtension.cs @@ -30,10 +30,25 @@ public ImmutableSortedDictionariesExtension(ISpecification specificati public IServiceRepository Get(IServiceRepository parameter) => parameter.DecorateContentsWith() .When(_specification) - .Decorate(); + .Decorate() + .Decorate(Register); + + IConstructorLocator Register(IServiceProvider arg1, IConstructorLocator previous) + => new ConstructorLocator(_specification, previous); void ICommand.Execute(IServices parameter) {} + sealed class ConstructorLocator : DictionaryConstructorLocator, IConstructorLocator + { + public ConstructorLocator(ISpecification specification, IConstructorLocator previous) + : base(specification, previous, typeof(Adapter<,>)) {} + } + + sealed class Adapter : Dictionary, IActivationAware + { + public object Get() => this.ToImmutableSortedDictionary(); + } + sealed class GenericTypes : IGenericTypes { readonly static TypeInfo Check = typeof(ImmutableSortedDictionary).GetTypeInfo(); diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs index 09d7b9043..84a490e79 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Types/ImmutableSortedSetExtension.cs @@ -8,6 +8,7 @@ using ExtendedXmlSerializer.Core.Specifications; using ExtendedXmlSerializer.ReflectionModel; using JetBrains.Annotations; +using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Reflection; @@ -27,10 +28,25 @@ sealed class ImmutableSortedSetExtension : ISerializerExtension public IServiceRepository Get(IServiceRepository parameter) => parameter.DecorateContentsWith() .When(_specification) - .Decorate(); + .Decorate() + .Decorate(Register); + + IConstructorLocator Register(IServiceProvider arg1, IConstructorLocator previous) + => new ConstructorLocator(_specification, previous); void ICommand.Execute(IServices parameter) {} + sealed class ConstructorLocator : ListConstructorLocator, IConstructorLocator + { + public ConstructorLocator(ISpecification specification, IConstructorLocator previous) + : base(specification, previous, typeof(Adapter<>)) {} + } + + sealed class Adapter : List, IActivationAware + { + public object Get() => this.ToImmutableSortedSet(); + } + sealed class GenericTypes : IGenericTypes { readonly static TypeInfo Check = typeof(ImmutableSortedSet).GetTypeInfo(); diff --git a/src/ExtendedXmlSerializer/ReflectionModel/DictionaryConstructorLocator.cs b/src/ExtendedXmlSerializer/ReflectionModel/DictionaryConstructorLocator.cs new file mode 100644 index 000000000..153a712fd --- /dev/null +++ b/src/ExtendedXmlSerializer/ReflectionModel/DictionaryConstructorLocator.cs @@ -0,0 +1,42 @@ +using ExtendedXmlSerializer.Core.Sources; +using ExtendedXmlSerializer.Core.Specifications; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace ExtendedXmlSerializer.ReflectionModel +{ + class DictionaryConstructorLocator : IParameterizedSource + { + readonly ISpecification _specification; + readonly IConstructorLocator _previous; + readonly Type _baseType; + + public DictionaryConstructorLocator(ISpecification specification, IConstructorLocator previous) + : this(specification, previous, typeof(Dictionary<,>)) {} + + public DictionaryConstructorLocator(ISpecification specification, IConstructorLocator previous, + Type baseType) + { + _specification = specification; + _previous = previous; + _baseType = baseType; + } + + public ConstructorInfo Get(TypeInfo parameter) + { + var accounted = _specification.IsSatisfiedBy(parameter) + ? MakeGenericType(parameter) + : parameter; + var result = _previous.Get(accounted); + return result; + } + + Type MakeGenericType(TypeInfo parameter) + { + var types = DictionaryPairTypesLocator.Default.Get(parameter); + var result = _baseType.MakeGenericType(types?.KeyType, types?.ValueType); + return result; + } + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs b/src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs index 49bd280cc..dd0bcb7e2 100644 --- a/src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs +++ b/src/ExtendedXmlSerializer/ReflectionModel/ListConstructorLocator.cs @@ -1,5 +1,6 @@ using ExtendedXmlSerializer.Core.Sources; using ExtendedXmlSerializer.Core.Specifications; +using System; using System.Collections.Generic; using System.Reflection; @@ -9,17 +10,23 @@ class ListConstructorLocator : IParameterizedSource { readonly ISpecification _specification; readonly IConstructorLocator _previous; + readonly Type _baseType; public ListConstructorLocator(ISpecification specification, IConstructorLocator previous) + : this(specification, previous, typeof(List<>)) {} + + public ListConstructorLocator(ISpecification specification, IConstructorLocator previous, + Type baseType) { _specification = specification; _previous = previous; + _baseType = baseType; } public ConstructorInfo Get(TypeInfo parameter) { var accounted = _specification.IsSatisfiedBy(parameter) - ? typeof(List<>).MakeGenericType(CollectionItemTypeLocator.Default.Get(parameter)) + ? _baseType.MakeGenericType(CollectionItemTypeLocator.Default.Get(parameter)) : parameter; var result = _previous.Get(accounted); return result; diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs index 2efcccac6..a724ae164 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -27,33 +27,6 @@ public void Verify() serializer.Cycle(instance).Should().BeEquivalentTo(instance); } - /*[Fact] - public void VerifyEmpty() - { - var serializer = new ConfigurationContainer().UseAutoFormatting() - .UseOptimizedNamespaces() - .EnableImmutableTypes() - .EnableParameterizedContent() - .Create() - .ForTesting(); - var instance = new SubjectList("Hello World!", ImmutableList.Empty); - - serializer.Cycle(instance).Should().BeEquivalentTo(instance); - }*/ - - class SubjectList - { - public SubjectList(string name, ImmutableList list) - { - Name = name; - List = list; - } - - public string Name { get; } - - public ImmutableList List { get; } - } - [Fact] public void VerifyHashSet() { @@ -136,5 +109,140 @@ public void VerifySortedDictionary() dictionary["Hello"].Should().Be(instance["Hello"]); } + + [Fact] + public void VerifyListEmpty() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create() + .ForTesting(); + var instance = new SubjectList("Hello World!", ImmutableList.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyHashSetEmpty() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create() + .ForTesting(); + var instance = new SubjectHashSet("Hello World!", ImmutableHashSet.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifySortedSetEmpty() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create() + .ForTesting(); + var instance = new SubjectSortedSet("Hello World!", ImmutableSortedSet.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyDictionaryEmpty() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create() + .ForTesting(); + var instance = new SubjectDictionary("Hello World!", ImmutableDictionary.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifySortedDictionaryEmpty() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create() + .ForTesting(); + var instance = new SubjectSortedDictionary("Hello World!", ImmutableSortedDictionary.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + class SubjectList + { + public SubjectList(string name, ImmutableList list) + { + Name = name; + List = list; + } + + public string Name { get; } + + public ImmutableList List { get; } + } + + class SubjectHashSet + { + public SubjectHashSet(string name, ImmutableHashSet list) + { + Name = name; + List = list; + } + + public string Name { get; } + + public ImmutableHashSet List { get; } + } + + class SubjectSortedSet + { + public SubjectSortedSet(string name, ImmutableSortedSet list) + { + Name = name; + List = list; + } + + public string Name { get; } + + public ImmutableSortedSet List { get; } + } + + class SubjectDictionary + { + public SubjectDictionary(string name, ImmutableDictionary list) + { + Name = name; + List = list; + } + + public string Name { get; } + + public ImmutableDictionary List { get; } + } + + class SubjectSortedDictionary + { + public SubjectSortedDictionary(string name, ImmutableSortedDictionary list) + { + Name = name; + List = list; + } + + public string Name { get; } + + public ImmutableSortedDictionary List { get; } + } } } \ No newline at end of file From 3cd715cc3b88c566606654135d4d66ffce33a4fb Mon Sep 17 00:00:00 2001 From: Mike-E Date: Sun, 6 Dec 2020 00:19:07 -0500 Subject: [PATCH 8/9] Added more tests for `EnableParameterizedContent*` --- .../AllMembersParameterizedActivators.cs | 5 +- .../Issue485Tests.cs | 177 +++++++++++++++++- 2 files changed, 177 insertions(+), 5 deletions(-) diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs index 6a8c535aa..aa89d2435 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs @@ -74,7 +74,10 @@ public IActivator Get(Func parameter) { var arguments = new Enumerable(_constructor.GetParameters() .Select(x => x.Name) - .Select(parameter)); + .Select(parameter) + .Select(x => x is IActivationAware aware + ? aware.Get() + : x)); var result = new ConstructedActivator(_constructor, arguments); return result; } diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs index a724ae164..cce2f09fc 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests.cs @@ -19,7 +19,7 @@ public void Verify() var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() .EnableImmutableTypes() - .EnableParameterizedContentWithPropertyAssignments() + .EnableParameterizedContent() .Create() .ForTesting(); serializer.Assert(instance, @@ -35,7 +35,7 @@ public void VerifyHashSet() var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() .EnableImmutableTypes() - .EnableParameterizedContentWithPropertyAssignments() + .EnableParameterizedContent() .Create() .ForTesting(); serializer.Assert(instance, @@ -51,7 +51,7 @@ public void VerifySortedSet() var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() .EnableImmutableTypes() - .EnableParameterizedContentWithPropertyAssignments() + .EnableParameterizedContent() .Create() .ForTesting(); serializer.Assert(instance, @@ -70,7 +70,7 @@ public void VerifyDictionary() var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() .EnableImmutableTypes() - .EnableParameterizedContentWithPropertyAssignments() + .EnableParameterizedContent() .Create() .ForTesting(); @@ -94,6 +94,105 @@ public void VerifySortedDictionary() ["Last"] = "Value2", }.ToImmutableSortedDictionary(); + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create() + .ForTesting(); + + serializer.Assert(instance, + @""); + + var dictionary = serializer.Cycle(instance); + dictionary.Should().BeEquivalentTo(instance); + + dictionary["Hello"].Should().Be(instance["Hello"]); + } + + [Fact] + public void VerifyWithPropertyAssignments() + { + var instance = new[] { 1, 2, 3, 4 }.ToImmutableList(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + serializer.Assert(instance, + @"1234"); + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyHashSetWithPropertyAssignments() + { + var instance = new[] { 1, 2, 3, 4 }.ToImmutableHashSet(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + serializer.Assert(instance, + @"1234"); + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifySortedSetWithPropertyAssignments() + { + var instance = new[] { 1, 2, 3, 4 }.ToImmutableSortedSet(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + serializer.Assert(instance, + @"1234"); + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyDictionaryWithPropertyAssignments() + { + var instance = new Dictionary + { + ["Hello"] = "World" + }.ToImmutableDictionary(); + + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + + serializer.Assert(instance, + @""); + + var dictionary = serializer.Cycle(instance); + dictionary.Should().BeEquivalentTo(instance); + + dictionary["Hello"].Should().Be(instance["Hello"]); + } + + [Fact] + public void VerifySortedDictionaryWithPropertyAssignments() + { + var instance = new Dictionary + { + ["First"] = "Value1", + ["Hello"] = "World", + ["Second"] = "Value2", + ["Last"] = "Value2", + }.ToImmutableSortedDictionary(); + var serializer = new ConfigurationContainer().UseAutoFormatting() .UseOptimizedNamespaces() .EnableImmutableTypes() @@ -180,6 +279,76 @@ public void VerifySortedDictionaryEmpty() serializer.Cycle(instance).Should().BeEquivalentTo(instance); } + [Fact] + public void VerifyListEmptyWithPropertyAssignments() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + var instance = new SubjectList("Hello World!", ImmutableList.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyHashSetEmptyWithPropertyAssignments() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + var instance = new SubjectHashSet("Hello World!", ImmutableHashSet.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifySortedSetEmptyWithPropertyAssignments() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + var instance = new SubjectSortedSet("Hello World!", ImmutableSortedSet.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyDictionaryEmptyWithPropertyAssignments() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + var instance = new SubjectDictionary("Hello World!", ImmutableDictionary.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifySortedDictionaryEmptyWithPropertyAssignments() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .UseOptimizedNamespaces() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create() + .ForTesting(); + var instance = new SubjectSortedDictionary("Hello World!", ImmutableSortedDictionary.Empty); + + serializer.Cycle(instance).Should().BeEquivalentTo(instance); + } + class SubjectList { public SubjectList(string name, ImmutableList list) From f5c59fd9dd08372ef9bf5dc52e7329e3ffcefca2 Mon Sep 17 00:00:00 2001 From: Mike-E Date: Sun, 6 Dec 2020 16:43:54 -0500 Subject: [PATCH 9/9] Resolving proper default values for parameterized content. --- .../AllMembersParameterizedActivators.cs | 3 +- .../Members/DefaultValueTypeDefaults.cs | 20 ++++++ .../Members/ParameterizedActivators.cs | 3 +- .../Members/ParameterizedAwareTypeDefaults.cs | 31 ++++++++ .../Issue485Tests_Extended.cs | 71 +++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Content/Members/DefaultValueTypeDefaults.cs create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedAwareTypeDefaults.cs create mode 100644 test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests_Extended.cs diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs index aa89d2435..af66b9ea0 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/AllMembersParameterizedActivators.cs @@ -24,7 +24,8 @@ sealed class AllMembersParameterizedActivators : IActivators public AllMembersParameterizedActivators(IActivators activators, IQueriedConstructors constructors, IConstructorMembers members, IMemberAccessors accessors, ITypeMembers typeMembers) - : this(activators, constructors, members, accessors, typeMembers, TypeDefaults.Defaults.Get(activators)) {} + : this(activators, constructors, members, accessors, typeMembers, + new ParameterizedAwareTypeDefaults(TypeDefaults.Defaults.Get(activators), constructors)) {} // ReSharper disable once TooManyDependencies public AllMembersParameterizedActivators(IActivators activators, IQueriedConstructors constructors, diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/DefaultValueTypeDefaults.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/DefaultValueTypeDefaults.cs new file mode 100644 index 000000000..edcd01161 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/DefaultValueTypeDefaults.cs @@ -0,0 +1,20 @@ +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.Core.Sources; +using ExtendedXmlSerializer.ReflectionModel; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel.Content.Members +{ + sealed class DefaultValueTypeDefaults : ITypeDefaults + { + public static DefaultValueTypeDefaults Default { get; } = new DefaultValueTypeDefaults(); + + DefaultValueTypeDefaults() : this(new Generic>(typeof(DefaultValues<>))) {} + + readonly IGeneric> _generic; + + public DefaultValueTypeDefaults(IGeneric> generic) => _generic = generic; + + public object Get(TypeInfo parameter) => _generic.Get(parameter)().Get(); + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs index 0156cd82c..e84476960 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedActivators.cs @@ -22,7 +22,8 @@ sealed class ParameterizedActivators : IActivators // ReSharper disable once TooManyDependencies public ParameterizedActivators(IActivators activators, IQueriedConstructors constructors, IConstructorMembers members, IMemberAccessors accessors) - : this(activators, constructors, members, accessors, TypeDefaults.Defaults.Get(activators)) {} + : this(activators, constructors, members, accessors, + new ParameterizedAwareTypeDefaults(TypeDefaults.Defaults.Get(activators), constructors)) {} // ReSharper disable once TooManyDependencies public ParameterizedActivators(IActivators activators, IQueriedConstructors constructors, diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedAwareTypeDefaults.cs b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedAwareTypeDefaults.cs new file mode 100644 index 000000000..1142a0195 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Content/Members/ParameterizedAwareTypeDefaults.cs @@ -0,0 +1,31 @@ +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.ExtensionModel.Types; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel.Content.Members +{ + sealed class ParameterizedAwareTypeDefaults : ITypeDefaults + { + readonly ITypeDefaults _previous; + readonly IQueriedConstructors _constructors; + readonly ITypeDefaults _defaults; + + public ParameterizedAwareTypeDefaults(ITypeDefaults previous, IQueriedConstructors constructors) + : this(previous, constructors, DefaultValueTypeDefaults.Default) {} + + public ParameterizedAwareTypeDefaults(ITypeDefaults previous, IQueriedConstructors constructors, + ITypeDefaults defaults) + { + _previous = previous; + _constructors = constructors; + _defaults = defaults; + } + + public object Get(TypeInfo parameter) + { + var defaults = _constructors.Get(parameter) != null ? _defaults : _previous; + var result = defaults.Get(parameter); + return result; + } + } +} \ No newline at end of file diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests_Extended.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests_Extended.cs new file mode 100644 index 000000000..8c1392c6d --- /dev/null +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue485Tests_Extended.cs @@ -0,0 +1,71 @@ +using ExtendedXmlSerializer.Configuration; +using FluentAssertions; +using LightInject; +using System.Xml; +using Xunit; +// ReSharper disable All + +namespace ExtendedXmlSerializer.Tests.ReportedIssues +{ +#nullable enable + public sealed class Issue485Tests_Extended + { + [Fact] + public void Verify() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .EnableImmutableTypes() + .EnableParameterizedContent() + .Create(); + var settings = new XmlWriterSettings { Indent = true }; + + var instance = new U("ciao", null); + var xml = serializer.Serialize(settings, instance); + + var deserialized = serializer.Deserialize(xml); + deserialized.Member.Should().BeNull(); + } + + [Fact] + public void VerifyAlternate() + { + var serializer = new ConfigurationContainer().UseAutoFormatting() + .EnableImmutableTypes() + .EnableParameterizedContentWithPropertyAssignments() + .Create(); + var settings = new XmlWriterSettings { Indent = true }; + + var instance = new U("ciao", null); + var xml = serializer.Serialize(settings, instance); + + var deserialized = serializer.Deserialize(xml); + deserialized.Member.Should().BeNull(); + } + + class U + { + public string Id { get; } + public T? Member { get; } + + public U(string id, T? member) + { + Id = id; + Member = member; + } + } + + class T + { + public string Name { get; } + public ImmutableList List { get; } + + public T(string name, ImmutableList list) + { + Name = name; + List = list; + } + } + + } + #nullable restore +}