From 32a2a2a69fbc289af73bc153e1db1ceea9075a1e Mon Sep 17 00:00:00 2001 From: neuecc Date: Thu, 8 Jun 2017 16:37:40 +0900 Subject: 1.3.0, add MessagePackFormatterAttribute --- README.md | 38 +++++- nuget/MessagePack.AspNetCoreMvcFormatter.nuspec | 4 +- nuget/MessagePack.ImmutableCollection.nuspec | 8 +- nuget/MessagePack.ReactiveProperty.nuspec | 8 +- nuget/MessagePack.UnityShims.nuspec | 8 +- nuget/MessagePack.nuspec | 2 +- nuget/push.bat | 10 +- sandbox/Sandbox/Program.cs | 27 ++++ sandbox/SharedData/packages.config | 2 +- .../MessagePack.UnityClient.csproj | 1 + src/MessagePack/Attributes.cs | 11 ++ .../Resolvers/AttributeFormatterResolver.cs | 40 ++++++ src/MessagePack/Resolvers/StandardResolver.cs | 4 +- tests/MessagePack.Tests/MessagePack.Tests.csproj | 1 + .../SpecifiedFormatterResolverTest.cs | 152 +++++++++++++++++++++ 15 files changed, 293 insertions(+), 23 deletions(-) create mode 100644 src/MessagePack/Resolvers/AttributeFormatterResolver.cs create mode 100644 tests/MessagePack.Tests/SpecifiedFormatterResolverTest.cs diff --git a/README.md b/README.md index 1f2e97c3..04d0d8a4 100644 --- a/README.md +++ b/README.md @@ -672,9 +672,10 @@ Extension Point(IFormatterResolver) | Resovler Name | Description | | --- | --- | | BuiltinResolver | Builtin primitive and standard classes resolver. It includes primitive(int, bool, string...) and there nullable, array and list. and some extra builtin types(Guid, Uri, BigInteger, etc...). | -| StandardResolver | Composited resolver . It resolves in the following order `builtin -> dynamic enum -> dynamic generic -> dynamic union -> dynamic object -> primitive object`. This is the default of MessagePackSerializer. | +| StandardResolver | Composited resolver . It resolves in the following order `builtin -> attribute -> dynamic enum -> dynamic generic -> dynamic union -> dynamic object -> primitive object`. This is the default of MessagePackSerializer. | | ContractlessStandardResolver | Composited `StandardResolver` -> `DynamicContractlessObjectResolver`. It enables contractless serialization. | | PrimitiveObjectResolver | MessagePack primitive object resolver. It is used fallback in `object` type and supports `bool`, `char`, `sbyte`, `byte`, `short`, `int`, `long`, `ushort`, `uint`, `ulong`, `float`, `double`, `DateTime`, `string`, `byte[]`, `ICollection`, `IDictionary`. | +| AttributeFormatterResolver | Get formatter from `[MessagePackFormatter]` attribute. | | CompositeResolver | Singleton helper of setup custom resolvers. You can use `Register` or `RegisterAndSetAsDefault` API. | | NativeDateTimeResolver | Serialize by .NET native DateTime binary format. | | OldSpecResolver | str and bin serialize/deserialize follows old messagepack spec(use raw format) | @@ -838,6 +839,40 @@ internal static class SampleCustomResolverGetFormatterHelper } ``` +MessaegPackFormatterAttribute +--- +MessaegPackFormatterAttribute is lightweight extension point of class, struct, interface, enum. This is like JSON.NET's JsonConverterAttribute. For example, serialize private field. + +```csharp +[MessagePackFormatter(typeof(CustomObjectFormatter))] +public class CustomObject +{ + string internalId; + + public CustomObject() + { + this.internalId = Guid.NewGuid().ToString(); + } + + // serialize/deserialize internal field. + class CustomObjectFormatter : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, CustomObject value, IFormatterResolver formatterResolver) + { + return formatterResolver.GetFormatterWithVerify().Serialize(ref bytes, offset, value.internalId, formatterResolver); + } + + public CustomObject Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var id = formatterResolver.GetFormatterWithVerify().Deserialize(bytes, offset, formatterResolver, out readSize); + return new CustomObject { internalId = id }; + } + } +} +``` + +Formatter is retrieved by `AttributeFormatterResolver`, it is included in `StandardResolver`. + for Unity --- You can install by package and includes source code. If build target as PC, you can use as is but if build target uses IL2CPP, you can not use `Dynamic***Resolver` so use pre-code generation. Please see [pre-code generation section](https://github.com/neuecc/MessagePack-CSharp#pre-code-generationunityxamarin-supports). @@ -935,6 +970,7 @@ MessagePack.Resolvers.CompositeResolver.RegisterAndSetAsDefault( // finally, use builtin/primitive resolver(don't use StandardResolver, it includes dynamic generation) MessagePack.Resolvers.BuiltinResolver.Instance, + AttributeFormatterResolver.Instance, MessagePack.Resolvers.PrimitiveObjectResolver.Instance ); ``` diff --git a/nuget/MessagePack.AspNetCoreMvcFormatter.nuspec b/nuget/MessagePack.AspNetCoreMvcFormatter.nuspec index 075dd8c1..bd11d0e2 100644 --- a/nuget/MessagePack.AspNetCoreMvcFormatter.nuspec +++ b/nuget/MessagePack.AspNetCoreMvcFormatter.nuspec @@ -2,7 +2,7 @@ MessagePack.AspNetCoreMvcFormatter - 1.2.3 + 1.3.0 ASP.NET Core MVC Input/Output MessagePack formatter neuecc neuecc @@ -13,7 +13,7 @@ MsgPack, MessagePack, Serialization, Formatter, Serializer, aspnetcore, aspnetcoremvc - + diff --git a/nuget/MessagePack.ImmutableCollection.nuspec b/nuget/MessagePack.ImmutableCollection.nuspec index 2342d4e4..d9b231fd 100644 --- a/nuget/MessagePack.ImmutableCollection.nuspec +++ b/nuget/MessagePack.ImmutableCollection.nuspec @@ -2,7 +2,7 @@ MessagePack.ImmutableCollection - 1.2.3 + 1.3.0 MessagePack for C# Extension Support for ImmutableCollection neuecc neuecc @@ -17,15 +17,15 @@ - + - + - + diff --git a/nuget/MessagePack.ReactiveProperty.nuspec b/nuget/MessagePack.ReactiveProperty.nuspec index f5ddb42e..855233b1 100644 --- a/nuget/MessagePack.ReactiveProperty.nuspec +++ b/nuget/MessagePack.ReactiveProperty.nuspec @@ -2,7 +2,7 @@ MessagePack.ReactiveProperty - 1.2.3 + 1.3.0 MessagePack for C# Extension Support for ReactiveProperty neuecc neuecc @@ -17,15 +17,15 @@ - + - + - + diff --git a/nuget/MessagePack.UnityShims.nuspec b/nuget/MessagePack.UnityShims.nuspec index bcbc3fb1..b6d8e7df 100644 --- a/nuget/MessagePack.UnityShims.nuspec +++ b/nuget/MessagePack.UnityShims.nuspec @@ -2,7 +2,7 @@ MessagePack.UnityShims - 1.2.3 + 1.3.0 MessagePack for C# Extension Support for Unity(add pseudo Vector type and fast Vectory[] extension formatter) neuecc neuecc @@ -17,13 +17,13 @@ - + - + - + diff --git a/nuget/MessagePack.nuspec b/nuget/MessagePack.nuspec index 141ead1d..8986ae71 100644 --- a/nuget/MessagePack.nuspec +++ b/nuget/MessagePack.nuspec @@ -2,7 +2,7 @@ MessagePack - 1.2.3 + 1.3.0 MessagePack for C# neuecc neuecc diff --git a/nuget/push.bat b/nuget/push.bat index afafe812..c153c253 100644 --- a/nuget/push.bat +++ b/nuget/push.bat @@ -1,6 +1,6 @@ -nuget push MessagePack.1.2.3.nupkg -Source https://www.nuget.org/api/v2/package -nuget push MessagePack.ImmutableCollection.1.2.3.nupkg -Source https://www.nuget.org/api/v2/package -nuget push MessagePack.ReactiveProperty.1.2.3.nupkg -Source https://www.nuget.org/api/v2/package -nuget push MessagePack.UnityShims.1.2.3.nupkg -Source https://www.nuget.org/api/v2/package -nuget push MessagePack.AspNetCoreMvcFormatter.1.2.3.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MessagePack.1.3.0.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MessagePack.ImmutableCollection.1.3.0.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MessagePack.ReactiveProperty.1.3.0.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MessagePack.UnityShims.1.3.0.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MessagePack.AspNetCoreMvcFormatter.1.3.0.nupkg -Source https://www.nuget.org/api/v2/package REM nuget push MessagePackAnalyzer.1.6.0.nupkg -Source https://www.nuget.org/api/v2/package \ No newline at end of file diff --git a/sandbox/Sandbox/Program.cs b/sandbox/Sandbox/Program.cs index 5e2f9873..e29f7547 100644 --- a/sandbox/Sandbox/Program.cs +++ b/sandbox/Sandbox/Program.cs @@ -228,6 +228,33 @@ namespace Sandbox } + [MessagePackFormatter(typeof(CustomObjectFormatter))] + public class CustomObject + { + string internalId; + + public CustomObject() + { + this.internalId = Guid.NewGuid().ToString(); + } + + // serialize/deserialize internal field. + class CustomObjectFormatter : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, CustomObject value, IFormatterResolver formatterResolver) + { + return formatterResolver.GetFormatterWithVerify().Serialize(ref bytes, offset, value.internalId, formatterResolver); + } + + public CustomObject Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var id = formatterResolver.GetFormatterWithVerify().Deserialize(bytes, offset, formatterResolver, out readSize); + return new CustomObject { internalId = id }; + } + } + } + + class Program { static void Main(string[] args) diff --git a/sandbox/SharedData/packages.config b/sandbox/SharedData/packages.config index 9b66c2a1..ad3acc03 100644 --- a/sandbox/SharedData/packages.config +++ b/sandbox/SharedData/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/MessagePack.UnityClient/MessagePack.UnityClient.csproj b/src/MessagePack.UnityClient/MessagePack.UnityClient.csproj index d306e757..703a924f 100644 --- a/src/MessagePack.UnityClient/MessagePack.UnityClient.csproj +++ b/src/MessagePack.UnityClient/MessagePack.UnityClient.csproj @@ -123,6 +123,7 @@ + diff --git a/src/MessagePack/Attributes.cs b/src/MessagePack/Attributes.cs index 324e55a1..c0dea5b8 100644 --- a/src/MessagePack/Attributes.cs +++ b/src/MessagePack/Attributes.cs @@ -53,4 +53,15 @@ namespace MessagePack { } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)] + public class MessagePackFormatterAttribute : Attribute + { + public Type FormatterType { get; private set; } + + public MessagePackFormatterAttribute(Type formatterType) + { + this.FormatterType = formatterType; + } + } } \ No newline at end of file diff --git a/src/MessagePack/Resolvers/AttributeFormatterResolver.cs b/src/MessagePack/Resolvers/AttributeFormatterResolver.cs new file mode 100644 index 00000000..8bdc3f1b --- /dev/null +++ b/src/MessagePack/Resolvers/AttributeFormatterResolver.cs @@ -0,0 +1,40 @@ +using MessagePack.Formatters; +using System; +using System.Reflection; + +namespace MessagePack.Resolvers +{ + /// + /// Get formatter from [MessaegPackFromatter] attribute. + /// + public class AttributeFormatterResolver : IFormatterResolver + { + public static IFormatterResolver Instance = new AttributeFormatterResolver(); + + AttributeFormatterResolver() + { + + } + + public IMessagePackFormatter GetFormatter() + { + return FormatterCache.formatter; + } + + static class FormatterCache + { + public static readonly IMessagePackFormatter formatter; + + static FormatterCache() + { + var attr = typeof(T).GetTypeInfo().GetCustomAttribute(); + if (attr == null) + { + return; + } + + formatter = (IMessagePackFormatter)Activator.CreateInstance(attr.FormatterType); + } + } + } +} \ No newline at end of file diff --git a/src/MessagePack/Resolvers/StandardResolver.cs b/src/MessagePack/Resolvers/StandardResolver.cs index 2fae4598..bd489e1f 100644 --- a/src/MessagePack/Resolvers/StandardResolver.cs +++ b/src/MessagePack/Resolvers/StandardResolver.cs @@ -3,7 +3,7 @@ namespace MessagePack.Resolvers { /// - /// Default composited resolver, builtin -> dynamic enum -> dynamic generic -> dynamic union -> dynamic object. + /// Default composited resolver, builtin -> attribute -> dynamic enum -> dynamic generic -> dynamic union -> dynamic object -> primitive. /// public class StandardResolver : IFormatterResolver { @@ -13,6 +13,8 @@ namespace MessagePack.Resolvers { BuiltinResolver.Instance, // Try Builtin + AttributeFormatterResolver.Instance, // Try use [MessagePackFormatter] + #if !NETSTANDARD1_4 MessagePack.Unity.UnityResolver.Instance, #endif diff --git a/tests/MessagePack.Tests/MessagePack.Tests.csproj b/tests/MessagePack.Tests/MessagePack.Tests.csproj index c177d815..e09f77e9 100644 --- a/tests/MessagePack.Tests/MessagePack.Tests.csproj +++ b/tests/MessagePack.Tests/MessagePack.Tests.csproj @@ -120,6 +120,7 @@ + diff --git a/tests/MessagePack.Tests/SpecifiedFormatterResolverTest.cs b/tests/MessagePack.Tests/SpecifiedFormatterResolverTest.cs new file mode 100644 index 00000000..ce0c693e --- /dev/null +++ b/tests/MessagePack.Tests/SpecifiedFormatterResolverTest.cs @@ -0,0 +1,152 @@ +using MessagePack.Formatters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace MessagePack.Tests +{ + + public class SpecifiedFormatterResolverTest + { + [MessagePackFormatter(typeof(NoObjectFormatter))] + class CustomClassObject + { + int X; + + public CustomClassObject(int x) + { + this.X = x; + } + + public int GetX() + { + return X; + } + + class NoObjectFormatter : IMessagePackFormatter + { + public CustomClassObject Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var r = MessagePackBinary.ReadInt32(bytes, offset, out readSize); + return new CustomClassObject(r); + } + + public int Serialize(ref byte[] bytes, int offset, CustomClassObject value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteInt32(ref bytes, offset, value.X); + } + } + } + + [MessagePackFormatter(typeof(CustomStructObjectFormatter))] + struct CustomStructObject + { + int X; + + public CustomStructObject(int x) + { + this.X = x; + } + + public int GetX() + { + return X; + } + + class CustomStructObjectFormatter : IMessagePackFormatter + { + public CustomStructObject Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var r = MessagePackBinary.ReadInt32(bytes, offset, out readSize); + return new CustomStructObject(r); + } + + public int Serialize(ref byte[] bytes, int offset, CustomStructObject value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteInt32(ref bytes, offset, value.X); + } + } + } + + [MessagePackFormatter(typeof(CustomEnumObjectFormatter))] + enum CustomyEnumObject + { + A = 0, B = 1, C = 2 + } + + class CustomEnumObjectFormatter : IMessagePackFormatter + { + public CustomyEnumObject Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var r = MessagePackBinary.ReadInt32(bytes, offset, out readSize); + if (r == 0) + { + return CustomyEnumObject.A; + } + else if (r == 2) + { + return CustomyEnumObject.C; + } + return CustomyEnumObject.B; + } + + public int Serialize(ref byte[] bytes, int offset, CustomyEnumObject value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteInt32(ref bytes, offset, (int)value); + } + } + + [MessagePackFormatter(typeof(CustomInterfaceObjectFormatter))] + interface ICustomInterfaceObject + { + int A { get; } + } + + class CustomInterfaceObjectFormatter : IMessagePackFormatter + { + public ICustomInterfaceObject Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var r = MessagePackBinary.ReadInt32(bytes, offset, out readSize); + return new InheritDefault(r); + } + + public int Serialize(ref byte[] bytes, int offset, ICustomInterfaceObject value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteInt32(ref bytes, offset, value.A); + } + } + + class InheritDefault : ICustomInterfaceObject + { + public int A { get; } + + public InheritDefault(int a) + { + this.A = a; + } + } + + class HogeMoge : ICustomInterfaceObject + { + public int A { get; set; } + } + + T Convert(T value) + { + return MessagePackSerializer.Deserialize(MessagePackSerializer.Serialize(value, MessagePack.Resolvers.StandardResolver.Instance), MessagePack.Resolvers.StandardResolver.Instance); + } + + [Fact] + public void CustomFormatters() + { + Convert(new CustomClassObject(999)).GetX().Is(999); + Convert(new CustomStructObject(1234)).GetX().Is(1234); + Convert(CustomyEnumObject.C).Is(CustomyEnumObject.C); + Convert((CustomyEnumObject)(1234)).Is(CustomyEnumObject.B); + Convert(new HogeMoge { A = 999 }).A.Is(999); + } + } +} -- cgit v1.2.3