-
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 basic serialization pipeline interception.
- Loading branch information
1 parent
7b3917e
commit e8cc4cf
Showing
5 changed files
with
235 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
16 changes: 16 additions & 0 deletions
16
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,16 @@ | ||
using ExtendedXmlSerializer.ContentModel.Format; | ||
using System; | ||
|
||
namespace ExtendedXmlSerializer.ExtensionModel.Instances | ||
{ | ||
public interface ISerializationInterceptor : ISerializationInterceptor<object> {} | ||
|
||
public interface ISerializationInterceptor<T> | ||
{ | ||
T Serializing(IFormatWriter writer, T instance); | ||
|
||
T Activating(Type instanceType); | ||
|
||
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); | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
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,62 @@ | ||
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 serializer = new ConfigurationContainer().Type<Subject>() | ||
.WithInterceptor(Interceptor.Default) | ||
.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 Interceptor : ISerializationInterceptor<Subject> | ||
{ | ||
public static Interceptor Default { get; } = new Interceptor(); | ||
|
||
Interceptor() {} | ||
|
||
public Subject Serializing(IFormatWriter writer, Subject instance) | ||
{ | ||
instance.Count++; | ||
return instance; | ||
} | ||
|
||
public Subject Activating(Type instanceType) => new ActivatedSubject(); | ||
|
||
public Subject Deserialized(IFormatReader reader, Subject instance) | ||
{ | ||
instance.Count++; | ||
return instance; | ||
} | ||
} | ||
|
||
sealed class ActivatedSubject : Subject {} | ||
|
||
class Subject | ||
{ | ||
public uint Count { get; set; } | ||
} | ||
} | ||
} |