-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
src/ExtendedXmlSerializer/ExtensionModel/Instances/ISerializationInterceptor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using ExtendedXmlSerializer.ContentModel.Format; | ||
using System; | ||
|
||
namespace ExtendedXmlSerializer.ExtensionModel.Instances | ||
{ | ||
/// <inheritdoc /> | ||
public interface ISerializationInterceptor : ISerializationInterceptor<object> {} | ||
|
||
/// <summary> | ||
/// Provides a mechanism to partake in the serialization pipeline. | ||
/// </summary> | ||
/// <typeparam name="T">The type to serialize.</typeparam> | ||
/// <seealso href="https://github.com/ExtendedXmlSerializer/home/issues/451" /> | ||
public interface ISerializationInterceptor<T> | ||
{ | ||
/// <summary> | ||
/// Called when an instance is being serialized. | ||
/// </summary> | ||
/// <param name="writer">The writer performing the serialization.</param> | ||
/// <param name="instance">The instance to serialize.</param> | ||
/// <returns>The actual instance to serialize.</returns> | ||
T Serializing(IFormatWriter writer, T instance); | ||
|
||
/// <summary> | ||
/// Returns the instance to activate during deserialization. If `null` is returned, the default internal infrastructure is used to activate the object. | ||
/// </summary> | ||
/// <param name="instanceType">The type to activate.</param> | ||
/// <returns>An instance of the provided type to activate. If `null`, the default internal activation is used.</returns> | ||
T Activating(Type instanceType); | ||
|
||
/// <summary> | ||
/// Called after an object has been activated and properties have been assigned their values from deserialization. | ||
/// </summary> | ||
/// <param name="reader">The reader used to deserialize the provided instance</param> | ||
/// <param name="instance">The instance that was deserialized.</param> | ||
/// <returns>The actual instance deserialized.</returns> | ||
T Deserialized(IFormatReader reader, T instance); | ||
} | ||
} |
131 changes: 131 additions & 0 deletions
131
src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationInterceptionExtension.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
using ExtendedXmlSerializer.ContentModel; | ||
using ExtendedXmlSerializer.ContentModel.Content; | ||
using ExtendedXmlSerializer.ContentModel.Format; | ||
using ExtendedXmlSerializer.Core; | ||
using ExtendedXmlSerializer.Core.Sources; | ||
using ExtendedXmlSerializer.ReflectionModel; | ||
using JetBrains.Annotations; | ||
using System; | ||
using System.Reflection; | ||
|
||
namespace ExtendedXmlSerializer.ExtensionModel.Instances | ||
{ | ||
sealed class SerializationInterceptionExtension | ||
: ISerializerExtension, IAssignable<TypeInfo, ISerializationInterceptor> | ||
{ | ||
readonly ITypedTable<ISerializationInterceptor> _registrations; | ||
|
||
[UsedImplicitly] | ||
public SerializationInterceptionExtension() : this(new TypedTable<ISerializationInterceptor>()) {} | ||
|
||
public SerializationInterceptionExtension(ITypedTable<ISerializationInterceptor> registrations) | ||
=> _registrations = registrations; | ||
|
||
public IServiceRepository Get(IServiceRepository parameter) => parameter.Decorate<IActivators>(Register) | ||
.Decorate<IContents>(Register); | ||
|
||
IContents Register(IServiceProvider _, IContents previous) => new Contents(_registrations, previous); | ||
|
||
IActivators Register(IServiceProvider _, IActivators previous) => new Activators(_registrations, previous); | ||
|
||
void ICommand<IServices>.Execute(IServices parameter) {} | ||
|
||
sealed class Contents : IContents | ||
{ | ||
readonly ITypedTable<ISerializationInterceptor> _interceptors; | ||
readonly IContents _contents; | ||
|
||
public Contents(ITypedTable<ISerializationInterceptor> interceptors, IContents contents) | ||
{ | ||
_interceptors = interceptors; | ||
_contents = contents; | ||
} | ||
|
||
public ISerializer Get(TypeInfo parameter) | ||
{ | ||
var previous = _contents.Get(parameter); | ||
var result = _interceptors.IsSatisfiedBy(parameter) ? Create(parameter, previous) : previous; | ||
return result; | ||
} | ||
|
||
Serializer Create(TypeInfo parameter, ISerializer previous) | ||
{ | ||
var interceptor = _interceptors.Get(parameter); | ||
var result = new Serializer(new Reader(interceptor, previous), new Writer(interceptor, previous)); | ||
return result; | ||
} | ||
} | ||
|
||
sealed class Activators : IActivators | ||
{ | ||
readonly ITypedTable<ISerializationInterceptor> _table; | ||
readonly IActivators _activators; | ||
|
||
public Activators(ITypedTable<ISerializationInterceptor> table, IActivators activators) | ||
{ | ||
_table = table; | ||
_activators = activators; | ||
} | ||
|
||
public IActivator Get(Type parameter) | ||
=> new Activator(parameter, _activators.Get(parameter), _table.Get(parameter)); | ||
|
||
sealed class Activator : IActivator | ||
{ | ||
readonly Type _instanceType; | ||
readonly IActivator _activator; | ||
readonly ISerializationInterceptor _interceptor; | ||
|
||
public Activator(Type instanceType, IActivator activator, ISerializationInterceptor interceptor) | ||
{ | ||
_instanceType = instanceType; | ||
_activator = activator; | ||
_interceptor = interceptor; | ||
} | ||
|
||
public object Get() => _interceptor.Activating(_instanceType) ?? _activator.Get(); | ||
} | ||
} | ||
|
||
sealed class Writer : IWriter | ||
{ | ||
readonly ISerializationInterceptor _interceptor; | ||
readonly IWriter _writer; | ||
|
||
public Writer(ISerializationInterceptor interceptor, IWriter writer) | ||
{ | ||
_interceptor = interceptor; | ||
_writer = writer; | ||
} | ||
|
||
public void Write(IFormatWriter writer, object instance) | ||
{ | ||
_writer.Write(writer, _interceptor.Serializing(writer, instance)); | ||
} | ||
} | ||
|
||
sealed class Reader : IReader | ||
{ | ||
readonly ISerializationInterceptor _interceptor; | ||
readonly IReader _reader; | ||
|
||
public Reader(ISerializationInterceptor interceptor, IReader reader) | ||
{ | ||
_interceptor = interceptor; | ||
_reader = reader; | ||
} | ||
|
||
public object Get(IFormatReader parameter) | ||
{ | ||
var instance = _reader.Get(parameter); | ||
var result = _interceptor.Deserialized(parameter, instance); | ||
return result; | ||
} | ||
} | ||
|
||
public void Assign(TypeInfo key, ISerializationInterceptor value) | ||
{ | ||
_registrations.Assign(key, value); | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationInterceptor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using ExtendedXmlSerializer.ContentModel.Format; | ||
using System; | ||
|
||
namespace ExtendedXmlSerializer.ExtensionModel.Instances | ||
{ | ||
sealed class SerializationInterceptor<T> : ISerializationInterceptor | ||
{ | ||
readonly ISerializationInterceptor<T> _interceptor; | ||
|
||
public SerializationInterceptor(ISerializationInterceptor<T> interceptor) => _interceptor = interceptor; | ||
|
||
public object Serializing(IFormatWriter writer, object instance) | ||
=> _interceptor.Serializing(writer, (T)instance); | ||
|
||
public object Activating(Type instanceType) => _interceptor.Activating(instanceType); | ||
|
||
public object Deserialized(IFormatReader reader, object instance) | ||
=> _interceptor.Deserialized(reader, (T)instance); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue451Tests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using ExtendedXmlSerializer.Configuration; | ||
using ExtendedXmlSerializer.ContentModel.Format; | ||
using ExtendedXmlSerializer.ExtensionModel.Instances; | ||
using ExtendedXmlSerializer.Tests.ReportedIssues.Support; | ||
using FluentAssertions; | ||
using System; | ||
using Xunit; | ||
|
||
namespace ExtendedXmlSerializer.Tests.ReportedIssues | ||
{ | ||
public sealed class Issue451Tests | ||
{ | ||
[Fact] | ||
public void Verify() | ||
{ | ||
var provider = new ServiceLocator(); | ||
var serializer = new ConfigurationContainer().Type<Subject>() | ||
.WithInterceptor(new Interceptor(provider)) | ||
.Create() | ||
.ForTesting(); | ||
|
||
var instance = new Subject(); | ||
|
||
instance.Count.Should().Be(0); | ||
const string content = | ||
@"<?xml version=""1.0"" encoding=""utf-8""?><Issue451Tests-Subject xmlns=""clr-namespace:ExtendedXmlSerializer.Tests.ReportedIssues;assembly=ExtendedXmlSerializer.Tests.ReportedIssues""><Count>1</Count></Issue451Tests-Subject>"; | ||
serializer.Assert(instance, content); | ||
instance.Count.Should().Be(1); | ||
|
||
var cycled = serializer.Deserialize<Subject>(content); | ||
cycled.Should().BeOfType<ActivatedSubject>(); | ||
cycled.Count.Should().Be(2); | ||
} | ||
|
||
sealed class ServiceLocator : IServiceProvider | ||
{ | ||
public object GetService(Type serviceType) => typeof(Subject).IsAssignableFrom(serviceType) | ||
? new ActivatedSubject() | ||
: throw new InvalidOperationException(); | ||
} | ||
|
||
sealed class Interceptor : ISerializationInterceptor<Subject> | ||
{ | ||
readonly IServiceProvider _provider; | ||
|
||
public Interceptor(IServiceProvider provider) => _provider = provider; | ||
|
||
public Subject Serializing(IFormatWriter writer, Subject instance) | ||
{ | ||
instance.Count++; | ||
return instance; | ||
} | ||
|
||
public Subject Activating(Type instanceType) => (Subject)_provider.GetService(instanceType); | ||
|
||
public Subject Deserialized(IFormatReader reader, Subject instance) | ||
{ | ||
instance.Count++; | ||
return instance; | ||
} | ||
} | ||
|
||
sealed class ActivatedSubject : Subject {} | ||
|
||
class Subject | ||
{ | ||
public uint Count { get; set; } | ||
} | ||
} | ||
} |