ContractlessStandardResolver supports typeof object object serialization #61

This commit is contained in:
neuecc 2017-06-22 18:37:39 +09:00
Родитель 2b56a7c106
Коммит abec6394ea
5 изменённых файлов: 201 добавлений и 1 удалений

Просмотреть файл

@ -32,6 +32,18 @@ namespace MessagePack.Formatters
}
public static bool IsSupportedType(Type type, TypeInfo typeInfo, object value)
{
if (value == null) return true;
if (typeToJumpCode.ContainsKey(type)) return true;
if (typeInfo.IsEnum) return true;
if (value is System.Collections.IDictionary) return true;
if (value is System.Collections.ICollection) return true;
return false;
}
public int Serialize(ref byte[] bytes, int offset, object value, IFormatterResolver formatterResolver)
{
if (value == null)

Просмотреть файл

@ -110,6 +110,11 @@ namespace MessagePack.Resolvers
static FormatterCache()
{
if (typeof(T) == typeof(object))
{
return;
}
var ti = typeof(T).GetTypeInfo();
if (ti.IsNullable())
{

Просмотреть файл

@ -1,4 +1,11 @@
using MessagePack.Formatters;
using System.Linq;
using MessagePack.Internal;
using System;
using System.Reflection;
using System.Collections.Generic;
using MessagePack.Resolvers;
using System.Linq.Expressions;
namespace MessagePack.Resolvers
{
@ -65,8 +72,26 @@ namespace MessagePack.Resolvers
static readonly IFormatterResolver[] resolvers = new[]
{
StandardResolver.Instance,
BuiltinResolver.Instance, // Try Builtin
AttributeFormatterResolver.Instance, // Try use [MessagePackFormatter]
#if !NETSTANDARD1_4
MessagePack.Unity.UnityResolver.Instance,
#endif
#if !ENABLE_IL2CPP
DynamicEnumResolver.Instance, // Try Enum
DynamicGenericResolver.Instance, // Try Array, Tuple, Collection
DynamicUnionResolver.Instance, // Try Union(Interface)
DynamicObjectResolver.Instance, // Try Object
#endif
DynamicContractlessObjectResolver.Instance,
// finally, try primitive -> dynamic contractless
DynamicObjectFallbackResolver.Instance
};
ContractlessStandardResolver()
@ -97,3 +122,109 @@ namespace MessagePack.Resolvers
}
}
}
namespace MessagePack.Internal
{
/// <summary>
/// In `object`, when serializing resolve by concrete type and when deserializing use primitive.
/// </summary>
internal class DynamicObjectFallbackResolver : IFormatterResolver
{
public static IFormatterResolver Instance = new DynamicObjectFallbackResolver();
DynamicObjectFallbackResolver()
{
}
public IMessagePackFormatter<T> GetFormatter<T>()
{
return FormatterCache<T>.formatter;
}
static class FormatterCache<T>
{
public static readonly IMessagePackFormatter<T> formatter;
static FormatterCache()
{
formatter = (typeof(T) == typeof(object))
? (IMessagePackFormatter<T>)(object)DynamicObjectFallbackResolverFormatter.Instance
: null;
}
}
}
internal class DynamicObjectFallbackResolverFormatter : IMessagePackFormatter<object>
{
internal delegate int SerializeMethod(object dynamicContractlessFormatter, ref byte[] bytes, int offset, object value, IFormatterResolver formatterResolver);
internal static readonly IMessagePackFormatter<object> Instance = new DynamicObjectFallbackResolverFormatter();
static readonly System.Collections.Generic.Dictionary<Type, KeyValuePair<object, SerializeMethod>> serializers = new Dictionary<Type, KeyValuePair<object, SerializeMethod>>();
DynamicObjectFallbackResolverFormatter()
{
}
public int Serialize(ref byte[] bytes, int offset, object value, IFormatterResolver formatterResolver)
{
if (value == null)
{
return MessagePackBinary.WriteNil(ref bytes, offset);
}
var type = value.GetType();
var ti = type.GetTypeInfo();
if (PrimitiveObjectFormatter.IsSupportedType(type, ti, value))
{
return PrimitiveObjectFormatter.Instance.Serialize(ref bytes, offset, value, formatterResolver);
}
KeyValuePair<object, SerializeMethod> formatterAndDelegate;
lock (serializers)
{
if (!serializers.TryGetValue(type, out formatterAndDelegate))
{
var formatter = DynamicContractlessObjectResolver.Instance.GetFormatterDynamic(type);
if (formatter == null)
{
throw new FormatterNotRegisteredException(type.FullName + " is not registered in this resolver. resolver:" + typeof(DynamicContractlessObjectResolver).Name);
}
var formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(type);
var param0 = Expression.Parameter(typeof(object), "formatter");
var param1 = Expression.Parameter(typeof(byte[]).MakeByRefType(), "bytes");
var param2 = Expression.Parameter(typeof(int), "offset");
var param3 = Expression.Parameter(typeof(object), "value");
var param4 = Expression.Parameter(typeof(IFormatterResolver), "formatterResolver");
var serializeMethodInfo = formatterType.GetRuntimeMethod("Serialize", new[] { typeof(byte[]).MakeByRefType(), typeof(int), type, typeof(IFormatterResolver) });
var body = Expression.Call(
Expression.Convert(param0, formatterType),
serializeMethodInfo,
param1,
param2,
ti.IsValueType ? Expression.Unbox(param3, type) : Expression.Convert(param3, type),
param4);
var lambda = Expression.Lambda<SerializeMethod>(body, param0, param1, param2, param3, param4).Compile();
formatterAndDelegate = new KeyValuePair<object, SerializeMethod>(formatter, lambda);
serializers[type] = formatterAndDelegate;
}
}
return formatterAndDelegate.Value(formatterAndDelegate.Key, ref bytes, offset, value, formatterResolver);
}
public object Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
{
return PrimitiveObjectFormatter.Instance.Deserialize(bytes, offset, formatterResolver, out readSize);
}
}
}

Просмотреть файл

@ -0,0 +1,51 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace MessagePack.Tests
{
public class ContractlessStandardResolverTest
{
public class Address
{
public string Street { get; set; }
}
public class Person
{
public string Name { get; set; }
public object[] /*Address*/ Addresses { get; set; }
}
[Fact]
public void SimpleTest()
{
var p = new Person
{
Name = "John",
Addresses = new[]
{
new Address { Street = "St." },
new Address { Street = "Ave." }
}
};
var result = MessagePack.MessagePackSerializer.Serialize(p, MessagePack.Resolvers.ContractlessStandardResolver.Instance);
MessagePackSerializer.ToJson(result).Is(@"{""Name"":""John"",""Addresses"":[{""Street"":""St.""},{""Street"":""Ave.""}]}");
var p2 = MessagePack.MessagePackSerializer.Deserialize<Person>(result, MessagePack.Resolvers.ContractlessStandardResolver.Instance);
p2.Name.Is("John");
var addresses = p2.Addresses as IList;
var d1 = addresses[0] as IDictionary;
var d2 = addresses[1] as IDictionary;
(d1["Street"] as string).Is("St.");
(d2["Street"] as string).Is("Ave.");
}
}
}

Просмотреть файл

@ -105,6 +105,7 @@
<ItemGroup>
<Compile Include="AnonymousTypeTest.cs" />
<Compile Include="CollectionTest.cs" />
<Compile Include="ContractlessStandardResolverTest.cs" />
<Compile Include="DictionaryTest.cs" />
<Compile Include="ExtensionTests\ImmutableCollectionTest.cs" />
<Compile Include="ExtensionTests\LZ4Test.cs" />