From c299fee56086f58868d6e34892a38756ebd3da9d Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 2 May 2014 17:49:13 +1200 Subject: [PATCH] -Refactor parameterized collection creation to use delegates --- Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs | 1 - .../Properties/AssemblyInfo.cs | 2 +- .../MetadataPropertyHandlingTests.cs | 27 +++++++++ .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonArrayContract.cs | 4 +- .../Serialization/JsonDictionaryContract.cs | 12 ++-- .../JsonSerializerInternalReader.cs | 16 ++---- .../Utilities/CollectionUtils.cs | 4 +- Src/Newtonsoft.Json/Utilities/FSharpUtils.cs | 56 +++++++------------ .../Utilities/ImmutableCollectionsUtils.cs | 12 ++-- 10 files changed, 71 insertions(+), 65 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index 6e6e1ed6..042150a8 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -52,7 +52,6 @@ namespace Newtonsoft.Json.Tests.Linq [TestFixture] public class JValueTests : TestFixtureBase { - [Test] public void UndefinedTests() { diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index bacc0854..d4ff6088 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -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")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs index 2d752955..27b3406a 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs @@ -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() { diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 0af92d13..286e5c28 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -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)] diff --git a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs index 5b1ae356..eb97759f 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs @@ -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 ParametrizedConstructor { get; private set; } /// /// Initializes a new instance of the class. @@ -198,7 +198,7 @@ namespace Newtonsoft.Json.Serialization #if !(NET20 || NET35 || NET40 || PORTABLE40) Type immutableCreatedType; - MethodBase immutableParameterizedCreator; + MethodCall immutableParameterizedCreator; if (ImmutableCollectionsUtils.TryBuildImmutableForArrayContract(underlyingType, CollectionItemType, out immutableCreatedType, out immutableParameterizedCreator)) { CreatedType = immutableCreatedType; diff --git a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs index 9f08d930..9b455319 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -70,7 +70,7 @@ namespace Newtonsoft.Json.Serialization private Func _genericTemporaryDictionaryCreator; internal bool ShouldCreateWrapper { get; private set; } - internal MethodBase ParametrizedConstructor { get; private set; } + internal MethodCall ParametrizedCreator { get; private set; } /// /// Initializes a new instance of the 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 immutableParameterizedCreator; if (ImmutableCollectionsUtils.TryBuildImmutableForDictionaryContract(underlyingType, DictionaryKeyType, DictionaryValueType, out immutableCreatedType, out immutableParameterizedCreator)) { CreatedType = immutableCreatedType; - ParametrizedConstructor = immutableParameterizedCreator; + ParametrizedCreator = immutableParameterizedCreator; IsReadOnlyOrFixedSize = true; } #endif diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index cc5b20cf..2474e316 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -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 - 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(); diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index 8851cfe1..7749e9d0 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -102,7 +102,7 @@ namespace Newtonsoft.Json.Utilities return false; } - public static ConstructorInfo ResolveEnumableCollectionConstructor(Type collectionType, Type collectionItemType) + public static MethodCall 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(constructor); } } diff --git a/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs b/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs index b84bdd41..9f1428b0 100644 --- a/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs @@ -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 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 CreateSeq(Type t) { MethodInfo seqType = _ofSeq.MakeGenericMethod(t); - return seqType; + return JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(seqType); } - public static MethodInfo CreateMap(Type keyType, Type valueType) + public static MethodCall 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)creatorGeneric.Invoke(null, null); } - } - - internal static class FSharpMapCreator - { - private static bool _initialized; - private static MethodCall _createMap; - - public static void EnsureInitialized(Assembly fsharpCoreAssembly) + public static MethodCall BuildMapCreator() { - if (!_initialized) + Type genericMapType = _mapType.MakeGenericType(typeof(TKey), typeof(TValue)); + ConstructorInfo ctor = genericMapType.GetConstructor(new[] { typeof(IEnumerable>) }); + MethodCall ctorDelegate = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(ctor); + + MethodCall creator = (target, args) => { - Type mapType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpMap`2"); + // convert dictionary KeyValuePairs to Tuples + IEnumerable> values = (IEnumerable>)args[0]; + IEnumerable> tupleValues = values.Select(kv => new Tuple(kv.Key, kv.Value)); - Type genericMapType = mapType.MakeGenericType(typeof(TKey), typeof(TValue)); + return ctorDelegate(null, tupleValues); + }; - ConstructorInfo ctor = genericMapType.GetConstructor(new[] { typeof(IEnumerable>) }); - - _createMap = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(ctor); - - _initialized = true; - } - } - - public static object CreateMapGeneric(IEnumerable> values) - { - IEnumerable> tupleValues = values.Select(kv => new Tuple(kv.Key, kv.Value)); - - object map = _createMap(null, tupleValues); - return map; + return creator; } } } diff --git a/Src/Newtonsoft.Json/Utilities/ImmutableCollectionsUtils.cs b/Src/Newtonsoft.Json/Utilities/ImmutableCollectionsUtils.cs index 11697dab..8893f6aa 100644 --- a/Src/Newtonsoft.Json/Utilities/ImmutableCollectionsUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ImmutableCollectionsUtils.cs @@ -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 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(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 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(method); return true; } } @@ -163,5 +166,4 @@ namespace Newtonsoft.Json.Utilities } } } - #endif \ No newline at end of file