ContractlessStandardResolver supports typeof object object serialization #61
This commit is contained in:
Родитель
2b56a7c106
Коммит
abec6394ea
|
@ -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" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче