-Refactor parameterized collection creation to use delegates

This commit is contained in:
James Newton-King
2014-05-02 17:49:13 +12:00
parent bbcc7e4d54
commit c299fee560
10 changed files with 71 additions and 65 deletions
@@ -52,7 +52,6 @@ namespace Newtonsoft.Json.Tests.Linq
[TestFixture]
public class JValueTests : TestFixtureBase
{
[Test]
public void UndefinedTests()
{
@@ -75,4 +75,4 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.3.17229")]
[assembly: AssemblyFileVersion("6.0.3.17302")]
@@ -43,6 +43,33 @@ namespace Newtonsoft.Json.Tests.Serialization
[TestFixture]
public class MetadataPropertyHandlingTests : TestFixtureBase
{
public class User
{
public string Name { get; set; }
}
[Test]
public void Demo()
{
string json = @"{
'Name': 'James',
'Password': 'Password1',
'$type': 'Newtonsoft.Json.Tests.Serialization.MetadataPropertyHandlingTests+User, Newtonsoft.Json.Tests'
}";
object o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
// no longer needs to be first
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
});
User u = (User)o;
Console.WriteLine(u.Name);
// James
}
[Test]
public void DeserializeArraysWithPreserveObjectReferences()
{
@@ -91,5 +91,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.3.17229")]
[assembly: AssemblyFileVersion("6.0.3.17302")]
[assembly: CLSCompliant(true)]
@@ -67,7 +67,7 @@ namespace Newtonsoft.Json.Serialization
internal bool IsArray { get; private set; }
internal bool ShouldCreateWrapper { get; private set; }
internal bool CanDeserialize { get; private set; }
internal MethodBase ParametrizedConstructor { get; private set; }
internal MethodCall<object, object> ParametrizedConstructor { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonArrayContract"/> class.
@@ -198,7 +198,7 @@ namespace Newtonsoft.Json.Serialization
#if !(NET20 || NET35 || NET40 || PORTABLE40)
Type immutableCreatedType;
MethodBase immutableParameterizedCreator;
MethodCall<object, object> immutableParameterizedCreator;
if (ImmutableCollectionsUtils.TryBuildImmutableForArrayContract(underlyingType, CollectionItemType, out immutableCreatedType, out immutableParameterizedCreator))
{
CreatedType = immutableCreatedType;
@@ -70,7 +70,7 @@ namespace Newtonsoft.Json.Serialization
private Func<object> _genericTemporaryDictionaryCreator;
internal bool ShouldCreateWrapper { get; private set; }
internal MethodBase ParametrizedConstructor { get; private set; }
internal MethodCall<object, object> ParametrizedCreator { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonDictionaryContract"/> class.
@@ -118,13 +118,13 @@ namespace Newtonsoft.Json.Serialization
if (keyType != null && valueType != null)
{
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(CreatedType, typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType));
ParametrizedCreator = CollectionUtils.ResolveEnumableCollectionConstructor(CreatedType, typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType));
#if !(NET35 || NET20 || NETFX_CORE)
if (ParametrizedConstructor == null && underlyingType.Name == FSharpUtils.FSharpMapTypeName)
if (ParametrizedCreator == null && underlyingType.Name == FSharpUtils.FSharpMapTypeName)
{
FSharpUtils.EnsureInitialized(underlyingType.Assembly());
ParametrizedConstructor = FSharpUtils.CreateMap(keyType, valueType);
ParametrizedCreator = FSharpUtils.CreateMap(keyType, valueType);
}
#endif
}
@@ -151,11 +151,11 @@ namespace Newtonsoft.Json.Serialization
#if !(NET20 || NET35 || NET40 || PORTABLE40)
Type immutableCreatedType;
MethodBase immutableParameterizedCreator;
MethodCall<object, object> immutableParameterizedCreator;
if (ImmutableCollectionsUtils.TryBuildImmutableForDictionaryContract(underlyingType, DictionaryKeyType, DictionaryValueType, out immutableCreatedType, out immutableParameterizedCreator))
{
CreatedType = immutableCreatedType;
ParametrizedConstructor = immutableParameterizedCreator;
ParametrizedCreator = immutableParameterizedCreator;
IsReadOnlyOrFixedSize = true;
}
#endif
@@ -471,11 +471,7 @@ namespace Newtonsoft.Json.Serialization
if (createdFromNonDefaultConstructor)
{
ConstructorInfo constructor = dictionaryContract.ParametrizedConstructor as ConstructorInfo;
if (constructor != null)
return constructor.Invoke(new object[] { dictionary });
return dictionaryContract.ParametrizedConstructor.Invoke(null, new object[] { dictionary });
return dictionaryContract.ParametrizedConstructor(null , new object[] { dictionary });
}
else if (dictionary is IWrappedDictionary)
{
@@ -763,7 +759,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (contract.OnErrorCallbacks.Count > 0)
throw JsonSerializationException.Create(reader, "Cannot call OnError on an array or readonly list, or list created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
if (arrayContract.ParametrizedConstructor == null && !arrayContract.IsArray)
if (arrayContract.ParametrizedCreator == null && !arrayContract.IsArray)
throw JsonSerializationException.Create(reader, "Cannot deserialize readonly or fixed size list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
}
@@ -787,11 +783,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
else
{
// call constructor that takes IEnumerable<T>
ConstructorInfo constructor = arrayContract.ParametrizedConstructor as ConstructorInfo;
if (constructor != null)
return constructor.Invoke(new object[] { list });
return arrayContract.ParametrizedConstructor.Invoke(null, new object[] { list });
return arrayContract.ParametrizedCreator(null, new object[] {list});
}
}
else if (list is IWrappedCollection)
@@ -1037,7 +1029,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
createdFromNonDefaultConstructor = false;
return (IList)list;
}
else if (contract.ParametrizedConstructor != null)
else if (contract.ParametrizedCreator != null)
{
createdFromNonDefaultConstructor = true;
return contract.CreateTemporaryCollection();
@@ -102,7 +102,7 @@ namespace Newtonsoft.Json.Utilities
return false;
}
public static ConstructorInfo ResolveEnumableCollectionConstructor(Type collectionType, Type collectionItemType)
public static MethodCall<object, object> ResolveEnumableCollectionConstructor(Type collectionType, Type collectionItemType)
{
Type genericEnumerable = typeof(IEnumerable<>).MakeGenericType(collectionItemType);
@@ -113,7 +113,7 @@ namespace Newtonsoft.Json.Utilities
if (parameters.Count == 1)
{
if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType))
return constructor;
return JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(constructor);
}
}
+21 -35
View File
@@ -40,6 +40,7 @@ namespace Newtonsoft.Json.Utilities
private static bool _initialized;
private static MethodInfo _ofSeq;
private static Type _mapType;
public static Assembly FSharpCoreAssembly { get; private set; }
public static MethodCall<object, object> IsUnion { get; private set; }
@@ -92,6 +93,8 @@ namespace Newtonsoft.Json.Utilities
Type listModule = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.ListModule");
_ofSeq = listModule.GetMethod("OfSeq");
_mapType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpMap`2");
#if !(NETFX_CORE || PORTABLE)
Thread.MemoryBarrier();
#endif
@@ -101,55 +104,38 @@ namespace Newtonsoft.Json.Utilities
}
}
public static MethodInfo CreateSeq(Type t)
public static MethodCall<object, object> CreateSeq(Type t)
{
MethodInfo seqType = _ofSeq.MakeGenericMethod(t);
return seqType;
return JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(seqType);
}
public static MethodInfo CreateMap(Type keyType, Type valueType)
public static MethodCall<object, object> CreateMap(Type keyType, Type valueType)
{
Type t = typeof(FSharpMapCreator<,>).MakeGenericType(keyType, valueType);
MethodInfo creatorDefinition = typeof (FSharpUtils).GetMethod("BuildMapCreator");
MethodInfo initializeMethod = t.GetMethod("EnsureInitialized");
MethodInfo creatorGeneric = creatorDefinition.MakeGenericMethod(keyType, valueType);
initializeMethod.Invoke(null, new object[] { FSharpCoreAssembly });
MethodInfo typedCreateMethod = t.GetMethod("CreateMapGeneric");
return typedCreateMethod;
return (MethodCall<object, object>)creatorGeneric.Invoke(null, null);
}
}
internal static class FSharpMapCreator<TKey, TValue>
{
private static bool _initialized;
private static MethodCall<object, object> _createMap;
public static void EnsureInitialized(Assembly fsharpCoreAssembly)
public static MethodCall<object, object> BuildMapCreator<TKey, TValue>()
{
if (!_initialized)
Type genericMapType = _mapType.MakeGenericType(typeof(TKey), typeof(TValue));
ConstructorInfo ctor = genericMapType.GetConstructor(new[] { typeof(IEnumerable<Tuple<TKey, TValue>>) });
MethodCall<object, object> ctorDelegate = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(ctor);
MethodCall<object, object> creator = (target, args) =>
{
Type mapType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpMap`2");
// convert dictionary KeyValuePairs to Tuples
IEnumerable<KeyValuePair<TKey, TValue>> values = (IEnumerable<KeyValuePair<TKey, TValue>>)args[0];
IEnumerable<Tuple<TKey, TValue>> tupleValues = values.Select(kv => new Tuple<TKey, TValue>(kv.Key, kv.Value));
Type genericMapType = mapType.MakeGenericType(typeof(TKey), typeof(TValue));
return ctorDelegate(null, tupleValues);
};
ConstructorInfo ctor = genericMapType.GetConstructor(new[] { typeof(IEnumerable<Tuple<TKey, TValue>>) });
_createMap = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(ctor);
_initialized = true;
}
}
public static object CreateMapGeneric(IEnumerable<KeyValuePair<TKey, TValue>> values)
{
IEnumerable<Tuple<TKey, TValue>> tupleValues = values.Select(kv => new Tuple<TKey, TValue>(kv.Key, kv.Value));
object map = _createMap(null, tupleValues);
return map;
return creator;
}
}
}
@@ -30,6 +30,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Serialization;
namespace Newtonsoft.Json.Utilities
{
@@ -101,7 +102,7 @@ namespace Newtonsoft.Json.Utilities
new ImmutableCollectionTypeInfo(ImmutableDictionaryGenericTypeName, ImmutableDictionaryGenericTypeName, ImmutableDictionaryTypeName)
};
internal static bool TryBuildImmutableForArrayContract(Type underlyingType, Type collectionItemType, out Type createdType, out MethodBase parameterizedCreator)
internal static bool TryBuildImmutableForArrayContract(Type underlyingType, Type collectionItemType, out Type createdType, out MethodCall<object, object> parameterizedCreator)
{
if (underlyingType.IsGenericType())
{
@@ -117,7 +118,8 @@ namespace Newtonsoft.Json.Utilities
if (mb != null)
{
createdType = createdTypeDefinition.MakeGenericType(collectionItemType);
parameterizedCreator = mb.MakeGenericMethod(collectionItemType);
MethodInfo method = mb.MakeGenericMethod(collectionItemType);
parameterizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(method);
return true;
}
}
@@ -129,7 +131,7 @@ namespace Newtonsoft.Json.Utilities
return false;
}
internal static bool TryBuildImmutableForDictionaryContract(Type underlyingType, Type keyItemType, Type valueItemType, out Type createdType, out MethodBase parameterizedCreator)
internal static bool TryBuildImmutableForDictionaryContract(Type underlyingType, Type keyItemType, Type valueItemType, out Type createdType, out MethodCall<object, object> parameterizedCreator)
{
if (underlyingType.IsGenericType())
{
@@ -150,7 +152,8 @@ namespace Newtonsoft.Json.Utilities
if (mb != null)
{
createdType = createdTypeDefinition.MakeGenericType(keyItemType, valueItemType);
parameterizedCreator = mb.MakeGenericMethod(keyItemType, valueItemType);
MethodInfo method = mb.MakeGenericMethod(keyItemType, valueItemType);
parameterizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(method);
return true;
}
}
@@ -163,5 +166,4 @@ namespace Newtonsoft.Json.Utilities
}
}
}
#endif