-
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.
Introduced type-based serialization interception. (#452)
- Loading branch information
1 parent
7b3917e
commit 39ae9e9
Showing
8 changed files
with
409 additions
and
12 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
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); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/ExtendedXmlSerializer/ExtensionModel/Instances/SerializationActivator.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 | ||
{ | ||
/// <summary> | ||
/// A generalized serialization interceptor base class for convenience. | ||
/// </summary> | ||
public abstract class SerializationActivator : ISerializationInterceptor | ||
{ | ||
/// <inheritdoc /> | ||
public virtual object Serializing(IFormatWriter writer, object instance) => instance; | ||
|
||
/// <inheritdoc /> | ||
public abstract object Activating(Type instanceType); | ||
|
||
/// <inheritdoc /> | ||
public virtual object Deserialized(IFormatReader reader, object instance) => instance; | ||
} | ||
} |
145 changes: 145 additions & 0 deletions
145
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,145 @@ | ||
using ExtendedXmlSerializer.ContentModel; | ||
using ExtendedXmlSerializer.ContentModel.Content; | ||
using ExtendedXmlSerializer.ContentModel.Format; | ||
using ExtendedXmlSerializer.Core; | ||
using ExtendedXmlSerializer.Core.Sources; | ||
using ExtendedXmlSerializer.Core.Specifications; | ||
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<IActivatingTypeSpecification>(Register) | ||
.Decorate<IActivators>(Register) | ||
.Decorate<IContents>(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<IServices>.Execute(IServices parameter) {} | ||
|
||
sealed class IsActivatingType : AnySpecification<TypeInfo>, IActivatingTypeSpecification | ||
{ | ||
public IsActivatingType(ISpecification<TypeInfo> specification, IActivatingTypeSpecification previous) | ||
: base(specification, previous) {} | ||
} | ||
|
||
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 _previous; | ||
|
||
public Activators(ITypedTable<ISerializationInterceptor> table, IActivators previous) | ||
{ | ||
_table = table; | ||
_previous = previous; | ||
} | ||
|
||
public IActivator Get(Type parameter) | ||
=> _table.IsSatisfiedBy(parameter) | ||
? new Activator(parameter, _previous.Build(parameter), _table.Get(parameter)) | ||
: _previous.Get(parameter); | ||
|
||
sealed class Activator : IActivator | ||
{ | ||
readonly Type _instanceType; | ||
readonly Func<IActivator> _activator; | ||
readonly ISerializationInterceptor _interceptor; | ||
|
||
public Activator(Type instanceType, Func<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); | ||
} | ||
} |
Oops, something went wrong.