From a00f7af862cb7081164d25075f5d3cf4358d1636 Mon Sep 17 00:00:00 2001 From: Mike-E Date: Mon, 21 Sep 2020 05:12:33 -0400 Subject: [PATCH] - Adjusted SerializationInterceptionExtension to account for activated types. - Added support for a generalized interceptor. --- .../ContentModel/RuntimeSerializer.cs | 9 +-- .../ExtensionMethodsForContent.cs | 15 +++- .../Instances/SerializationActivator.cs | 20 +++++ .../SerializationInterceptionExtension.cs | 34 +++++--- .../Issue451Tests_Reported.cs | 80 +++++++++++++++++++ 5 files changed, 140 insertions(+), 18 deletions(-) create mode 100644 src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationActivator.cs create mode 100644 test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue451Tests_Reported.cs diff --git a/src/ExtendedXmlSerializer/ContentModel/RuntimeSerializer.cs b/src/ExtendedXmlSerializer/ContentModel/RuntimeSerializer.cs index f5d3edb92..6cea5a4bd 100644 --- a/src/ExtendedXmlSerializer/ContentModel/RuntimeSerializer.cs +++ b/src/ExtendedXmlSerializer/ContentModel/RuntimeSerializer.cs @@ -2,7 +2,6 @@ using ExtendedXmlSerializer.ContentModel.Format; using ExtendedXmlSerializer.ContentModel.Reflection; using JetBrains.Annotations; -using System.Reflection; namespace ExtendedXmlSerializer.ContentModel { @@ -20,13 +19,9 @@ public RuntimeSerializer(IRuntimeSerialization serialization, IClassification cl public void Write(IFormatWriter writer, object instance) { - var typeInfo = instance.GetType() - .GetTypeInfo(); - _serialization.Get(typeInfo) - .Write(writer, instance); + _serialization.Get(instance.GetType()).Write(writer, instance); } - public object Get(IFormatReader reader) => _serialization.Get(_classification.Get(reader)) - .Get(reader); + public object Get(IFormatReader reader) => _serialization.Get(_classification.Get(reader)).Get(reader); } } \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionMethodsForContent.cs b/src/ExtendedXmlSerializer/ExtensionMethodsForContent.cs index ba6960106..610fdf7be 100644 --- a/src/ExtendedXmlSerializer/ExtensionMethodsForContent.cs +++ b/src/ExtendedXmlSerializer/ExtensionMethodsForContent.cs @@ -105,8 +105,21 @@ public static ITypeConfiguration WithMonitor(this ITypeConfiguration @t /// public static ITypeConfiguration WithInterceptor(this ITypeConfiguration @this, ISerializationInterceptor interceptor) + => @this.WithInterceptor(new SerializationInterceptor(interceptor)); + + /// + /// Applies a generalized interceptor for type configuration. An interceptor participates in the serialization pipeline by being + /// introduced during key serialization and deserialization events. + /// + /// The type to intercept. + /// The interceptor to apply to a type. + /// The type argument of the type configuration being configured. + /// The configured type configuration. + /// + public static ITypeConfiguration WithInterceptor(this ITypeConfiguration @this, + ISerializationInterceptor interceptor) => @this.Root.With() - .Apply(Support.Metadata, new SerializationInterceptor(interceptor)) + .Apply(Support.Metadata, interceptor) .Return(@this); /// diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationActivator.cs b/src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationActivator.cs new file mode 100644 index 000000000..ddac0d753 --- /dev/null +++ b/src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationActivator.cs @@ -0,0 +1,20 @@ +using ExtendedXmlSerializer.ContentModel.Format; +using System; + +namespace ExtendedXmlSerializer.ExtensionModel.Instances +{ + /// + /// A generalized serialization interceptor base class for convenience. + /// + public abstract class SerializationActivator : ISerializationInterceptor + { + /// + public virtual object Serializing(IFormatWriter writer, object instance) => instance; + + /// + public abstract object Activating(Type instanceType); + + /// + public virtual object Deserialized(IFormatReader reader, object instance) => instance; + } +} \ No newline at end of file diff --git a/src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationInterceptionExtension.cs b/src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationInterceptionExtension.cs index 794c9f444..acedb5b4d 100644 --- a/src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationInterceptionExtension.cs +++ b/src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationInterceptionExtension.cs @@ -3,6 +3,7 @@ using ExtendedXmlSerializer.ContentModel.Format; using ExtendedXmlSerializer.Core; using ExtendedXmlSerializer.Core.Sources; +using ExtendedXmlSerializer.Core.Specifications; using ExtendedXmlSerializer.ReflectionModel; using JetBrains.Annotations; using System; @@ -21,15 +22,26 @@ public SerializationInterceptionExtension() : this(new TypedTable registrations) => _registrations = registrations; - public IServiceRepository Get(IServiceRepository parameter) => parameter.Decorate(Register) - .Decorate(Register); + public IServiceRepository Get(IServiceRepository parameter) + => parameter.Decorate(Register) + .Decorate(Register) + .Decorate(Register); IContents Register(IServiceProvider _, IContents previous) => new Contents(_registrations, previous); + IActivatingTypeSpecification Register(IServiceProvider _, IActivatingTypeSpecification previous) + => new IsActivatingType(_registrations, previous); + IActivators Register(IServiceProvider _, IActivators previous) => new Activators(_registrations, previous); void ICommand.Execute(IServices parameter) {} + sealed class IsActivatingType : AnySpecification, IActivatingTypeSpecification + { + public IsActivatingType(ISpecification specification, IActivatingTypeSpecification previous) + : base(specification, previous) {} + } + sealed class Contents : IContents { readonly ITypedTable _interceptors; @@ -59,31 +71,33 @@ Serializer Create(TypeInfo parameter, ISerializer previous) sealed class Activators : IActivators { readonly ITypedTable _table; - readonly IActivators _activators; + readonly IActivators _previous; - public Activators(ITypedTable table, IActivators activators) + public Activators(ITypedTable table, IActivators previous) { - _table = table; - _activators = activators; + _table = table; + _previous = previous; } public IActivator Get(Type parameter) - => new Activator(parameter, _activators.Get(parameter), _table.Get(parameter)); + => _table.IsSatisfiedBy(parameter) + ? new Activator(parameter, _previous.Build(parameter), _table.Get(parameter)) + : _previous.Get(parameter); sealed class Activator : IActivator { readonly Type _instanceType; - readonly IActivator _activator; + readonly Func _activator; readonly ISerializationInterceptor _interceptor; - public Activator(Type instanceType, IActivator activator, ISerializationInterceptor interceptor) + public Activator(Type instanceType, Func activator, ISerializationInterceptor interceptor) { _instanceType = instanceType; _activator = activator; _interceptor = interceptor; } - public object Get() => _interceptor.Activating(_instanceType) ?? _activator.Get(); + public object Get() => _interceptor.Activating(_instanceType) ?? _activator().Get(); } } diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue451Tests_Reported.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue451Tests_Reported.cs new file mode 100644 index 000000000..7715a0405 --- /dev/null +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue451Tests_Reported.cs @@ -0,0 +1,80 @@ +using ExtendedXmlSerializer.Configuration; +using ExtendedXmlSerializer.ExtensionModel.Instances; +using FluentAssertions; +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using Xunit; + +namespace ExtendedXmlSerializer.Tests.ReportedIssues +{ + public sealed class Issue451Tests_Reported + { + [Fact] + public void TestInterceptor() + { + // language=XML + const string xml = @" + + true + + + ISO + + + "; + + var serializer = new ConfigurationContainer().EnableImplicitTyping(typeof(Processor)) + .Type() + .WithInterceptor(new Interceptor()) + .Create(); + + var contentStream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + using var reader = XmlReader.Create(contentStream); + var processor = (Processor)serializer.Deserialize(reader); + processor.Should().NotBeNull(); + processor.Enabled.Should().BeTrue(); + processor.Filters.Should().NotBeEmpty(); + processor.Filters.Only().Type.Should().Be("ISO"); + } + + public class Interceptor : SerializationActivator + { + public override object Activating(Type instanceType) + { + // processor should be retrieved from IoC container, but created manually for simplicity of test + var processor = new Processor(new Service()); + return processor; + } + } + + public interface IService {} + + class Service : IService {} + + public class Processor + { + // ReSharper disable once NotAccessedField.Local + readonly IService _service; + + public bool Enabled { get; set; } + + public List Filters { [UsedImplicitly] get; set; } + + public Processor(IService service) + { + _service = service; + + Filters = new List(); + } + } + + public class Filter + { + public string Type { get; set; } + } + } +} \ No newline at end of file