diff --git a/src/ExtendedXmlSerializer/ContentModel/Collections/CollectionSpecification.cs b/src/ExtendedXmlSerializer/ContentModel/Collections/CollectionSpecification.cs index 331a8faad..8d4792d75 100644 --- a/src/ExtendedXmlSerializer/ContentModel/Collections/CollectionSpecification.cs +++ b/src/ExtendedXmlSerializer/ContentModel/Collections/CollectionSpecification.cs @@ -6,7 +6,7 @@ namespace ExtendedXmlSerializer.ContentModel.Collections { class CollectionSpecification : DecoratedSpecification { - public CollectionSpecification(ISpecification specification) + protected CollectionSpecification(ISpecification specification) : base(IsCollectionTypeSpecification.Default.And(specification)) {} } } \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ContentModel/Identification/Identities.cs b/src/ExtendedXmlSerializer/ContentModel/Identification/Identities.cs index 82c044369..6d5825b4d 100644 --- a/src/ExtendedXmlSerializer/ContentModel/Identification/Identities.cs +++ b/src/ExtendedXmlSerializer/ContentModel/Identification/Identities.cs @@ -7,8 +7,6 @@ namespace ExtendedXmlSerializer.ContentModel.Identification { sealed class Identities : CacheBase, IIdentities { - readonly static TypeNameFormatter TypeNameFormatter = TypeNameFormatter.Default; - readonly IIdentityStore _source; readonly INames _alias; readonly ITypeFormatter _formatter; @@ -16,7 +14,7 @@ sealed class Identities : CacheBase, IIdentities [UsedImplicitly] public Identities(IIdentifiers identifiers, IIdentityStore source, INames names) - : this(source, names, TypeNameFormatter, identifiers) {} + : this(source, names, TypeNameFormatter.Default, identifiers) {} // ReSharper disable once TooManyDependencies public Identities(IIdentityStore source, INames alias, ITypeFormatter formatter, IIdentifiers identifiers) diff --git a/src/ExtendedXmlSerializer/ContentModel/Reflection/GenericNameParser.cs b/src/ExtendedXmlSerializer/ContentModel/Reflection/GenericNameParser.cs index fbce8ef30..2e31e5430 100644 --- a/src/ExtendedXmlSerializer/ContentModel/Reflection/GenericNameParser.cs +++ b/src/ExtendedXmlSerializer/ContentModel/Reflection/GenericNameParser.cs @@ -10,13 +10,7 @@ sealed class GenericNameParser : Parsing GenericNameParser() : this(CodeIdentifier.Default, Parse.Char(DefaultClrDelimiters.Default.Generic)) {} - public GenericNameParser(Parser identifier, Parser delimiter) : base( - identifier - .SelectMany(delimiter - .Optional() - .Accept, - (name, _) - => name) - ) {} + public GenericNameParser(Parser identifier, Parser delimiter) + : base(identifier.SelectMany(delimiter.Optional().Accept, (name, _) => name)) {} } } \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs index 322c4b692..d1d9a240b 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/DefaultExtensions.cs @@ -58,6 +58,7 @@ public override IEnumerator GetEnumerator() yield return new AllowedMembersExtension(_metadata); yield return new AllowedMemberValuesExtension(); yield return new MemberFormatExtension(); + yield return GeneratedListAwareExtension.Default; yield return ImmutableArrayExtension.Default; yield return SerializationExtension.Default; yield return RecursionAwareExtension.Default; diff --git a/src/ExtendedXmlSerializer/ExtensionModel/GeneratedListAwareExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/GeneratedListAwareExtension.cs new file mode 100644 index 000000000..65d1cb3ba --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/GeneratedListAwareExtension.cs @@ -0,0 +1,45 @@ +using ExtendedXmlSerializer.ContentModel.Conversion; +using ExtendedXmlSerializer.ContentModel.Reflection; +using ExtendedXmlSerializer.Core.Sources; +using ExtendedXmlSerializer.Core.Specifications; +using System; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel; + +sealed class GeneratedListAwareExtension : ISerializerExtension +{ + public static GeneratedListAwareExtension Default { get; } = new(); + + GeneratedListAwareExtension() {} + + public IServiceRepository Get(IServiceRepository parameter) + => parameter.Decorate(); + + public void Execute(IServices parameter) {} + + sealed class TypePartResolver : ITypePartResolver + { + readonly ITypePartResolver _previous; + readonly ISpecification _specification; + readonly IAlteration _alter; + + public TypePartResolver(ITypePartResolver previous) + : this(previous, IsGeneratedList.Default, GeneratedSubstitute.Default) {} + + public TypePartResolver(ITypePartResolver previous, ISpecification specification, + IAlteration alter) + { + _previous = previous; + _specification = specification; + _alter = alter; + } + + public TypeParts Get(TypeInfo parameter) + { + var type = _specification.IsSatisfiedBy(parameter) ? _alter.Get(parameter) : parameter; + var result = _previous.Get(type); + return result; + } + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionModel/GeneratedSubstitute.cs b/src/ExtendedXmlSerializer/ExtensionModel/GeneratedSubstitute.cs new file mode 100644 index 000000000..2f74c302e --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/GeneratedSubstitute.cs @@ -0,0 +1,29 @@ +using ExtendedXmlSerializer.Core.Sources; +using ExtendedXmlSerializer.ReflectionModel; +using System; +using System.Collections.Generic; + +namespace ExtendedXmlSerializer.ExtensionModel; + +sealed class GeneratedSubstitute : IAlteration +{ + public static GeneratedSubstitute Default { get; } = new(); + + GeneratedSubstitute() : this(typeof(List<>), CollectionItemTypeLocator.Default) {} + + readonly Type _definition; + readonly ICollectionItemTypeLocator _locator; + + public GeneratedSubstitute(Type definition, ICollectionItemTypeLocator locator) + { + _definition = definition; + _locator = locator; + } + + public Type Get(Type parameter) + { + var arguments = _locator.Get(parameter); + var result = _definition.MakeGenericType(arguments); + return result; + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionModel/IsGeneratedList.cs b/src/ExtendedXmlSerializer/ExtensionModel/IsGeneratedList.cs new file mode 100644 index 000000000..565b27572 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/IsGeneratedList.cs @@ -0,0 +1,14 @@ +using ExtendedXmlSerializer.Core.Specifications; +using System.Reflection; + +namespace ExtendedXmlSerializer.ExtensionModel; + +sealed class IsGeneratedList : DelegatedSpecification +{ + public static IsGeneratedList Default { get; } = new(); + + IsGeneratedList() + : base(x => x.FullName != null && + (x.FullName.StartsWith("<>z__ReadOnlySingleElementList") || + x.FullName.StartsWith("<>z__ReadOnlyArray"))) {} +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs b/src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs index 8b4515700..c44016997 100644 --- a/src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs +++ b/src/ExtendedXmlSerializer/ReflectionModel/CollectionAwareConstructorLocator.cs @@ -1,21 +1,14 @@ using ExtendedXmlSerializer.Core.Specifications; -using System.Collections.Generic; +using ExtendedXmlSerializer.ExtensionModel; using System.Reflection; namespace ExtendedXmlSerializer.ReflectionModel { sealed class CollectionAwareConstructorLocator : ListConstructorLocator, IConstructorLocator { - readonly static ISpecification Specification = IsInterface.Default.And(IsCollectionType.Instance); + readonly static ISpecification Specification + = IsInterface.Default.Or(IsGeneratedList.Default).And(IsCollectionTypeExpandedSpecification.Default); 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/IsCollectionTypeExpandedSpecification.cs b/src/ExtendedXmlSerializer/ReflectionModel/IsCollectionTypeExpandedSpecification.cs new file mode 100644 index 000000000..4446ea627 --- /dev/null +++ b/src/ExtendedXmlSerializer/ReflectionModel/IsCollectionTypeExpandedSpecification.cs @@ -0,0 +1,15 @@ +using ExtendedXmlSerializer.Core.Specifications; +using System.Collections.Generic; +using System.Reflection; + +namespace ExtendedXmlSerializer.ReflectionModel; + +sealed class IsCollectionTypeExpandedSpecification : AnySpecification +{ + public static IsCollectionTypeExpandedSpecification Default { get; } = new(); + + IsCollectionTypeExpandedSpecification() + : base(IsCollectionTypeSpecification.Default, + new IsAssignableGenericSpecification(typeof(IReadOnlyList<>)), + new IsAssignableGenericSpecification(typeof(IReadOnlyCollection<>))) {} +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ReflectionModel/IsUnspeakable.cs b/src/ExtendedXmlSerializer/ReflectionModel/IsUnspeakable.cs index 1cf114c98..6b8388598 100644 --- a/src/ExtendedXmlSerializer/ReflectionModel/IsUnspeakable.cs +++ b/src/ExtendedXmlSerializer/ReflectionModel/IsUnspeakable.cs @@ -10,6 +10,6 @@ sealed class IsUnspeakable : DelegatedSpecification { public static IsUnspeakable Default { get; } = new IsUnspeakable(); - IsUnspeakable() : base(x => x.Name.StartsWith("<")) {} + IsUnspeakable() : base(x => x.Name.StartsWith("<") || (x.FullName is not null && x.FullName.StartsWith("<"))) {} } } \ No newline at end of file diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue637Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue637Tests.cs new file mode 100644 index 000000000..cf4712a1b --- /dev/null +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue637Tests.cs @@ -0,0 +1,38 @@ +#if CORE +using ExtendedXmlSerializer.Configuration; +using ExtendedXmlSerializer.Tests.ReportedIssues.Support; +using FluentAssertions; +using System.Collections.Generic; +using Xunit; + +namespace ExtendedXmlSerializer.Tests.ReportedIssues +{ + public sealed class Issue637Tests + { + [Fact] + public void Verify() + { + var sut = new ConfigurationContainer().Create().ForTesting(); + var instance = new Class2(); + sut.Cycle(instance).Should().BeEquivalentTo(instance); + } + + [Fact] + public void VerifyModified() + { + var sut = new ConfigurationContainer().Create().ForTesting(); + var instance = new Class2 { List2 = [23]}; + sut.Cycle(instance).Should().BeEquivalentTo(instance); + } + + sealed class Class2 + { + public IReadOnlyList List1 { get; set; } = []; //this works + public IReadOnlyList List2 { get; set; } = [1]; //this breaks Serialize&Deserialize + public IList List3 { get; set; } = [1,2]; //this works + public IReadOnlyList List4 { get; set; } = new List(){1}; //this works (workaround) + } + + } +} +#endif \ No newline at end of file