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)
|
public int Serialize(ref byte[] bytes, int offset, object value, IFormatterResolver formatterResolver)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
|
|
|
@ -110,6 +110,11 @@ namespace MessagePack.Resolvers
|
||||||
|
|
||||||
static FormatterCache()
|
static FormatterCache()
|
||||||
{
|
{
|
||||||
|
if (typeof(T) == typeof(object))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var ti = typeof(T).GetTypeInfo();
|
var ti = typeof(T).GetTypeInfo();
|
||||||
if (ti.IsNullable())
|
if (ti.IsNullable())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
using MessagePack.Formatters;
|
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
|
namespace MessagePack.Resolvers
|
||||||
{
|
{
|
||||||
|
@ -65,8 +72,26 @@ namespace MessagePack.Resolvers
|
||||||
|
|
||||||
static readonly IFormatterResolver[] resolvers = new[]
|
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,
|
DynamicContractlessObjectResolver.Instance,
|
||||||
|
|
||||||
|
// finally, try primitive -> dynamic contractless
|
||||||
|
DynamicObjectFallbackResolver.Instance
|
||||||
};
|
};
|
||||||
|
|
||||||
ContractlessStandardResolver()
|
ContractlessStandardResolver()
|
||||||
|
@ -96,4 +121,110 @@ 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>
|
<ItemGroup>
|
||||||
<Compile Include="AnonymousTypeTest.cs" />
|
<Compile Include="AnonymousTypeTest.cs" />
|
||||||
<Compile Include="CollectionTest.cs" />
|
<Compile Include="CollectionTest.cs" />
|
||||||
|
<Compile Include="ContractlessStandardResolverTest.cs" />
|
||||||
<Compile Include="DictionaryTest.cs" />
|
<Compile Include="DictionaryTest.cs" />
|
||||||
<Compile Include="ExtensionTests\ImmutableCollectionTest.cs" />
|
<Compile Include="ExtensionTests\ImmutableCollectionTest.cs" />
|
||||||
<Compile Include="ExtensionTests\LZ4Test.cs" />
|
<Compile Include="ExtensionTests\LZ4Test.cs" />
|
||||||
|
|
Загрузка…
Ссылка в новой задаче