From 4605894b6b3c34859baa0561135e75b5ff8895c5 Mon Sep 17 00:00:00 2001 From: TylerBrinkley Date: Mon, 8 Aug 2016 09:18:34 -0500 Subject: [PATCH 1/3] Addresses #926 and #949 --- .../Serialization/JsonSerializerTest.cs | 17 ++++ .../DefaultSerializationBinder.cs | 81 ++++++++++++++++++- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 2d2d9797..973b49dd 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -9618,6 +9618,23 @@ Path '', line 1, position 1."); "Cannot populate list type System.Net.Mime.HeaderCollection. Path 'Headers', line 26, position 14."); } + [Test] + public void DeserializeDictionaryOfHashSetsWithTypeNameHandlingAll() + { + var dictionary = new Dictionary> + { + { 1, new HashSet(new[] { "test" }) }, + }; + + var jsonSerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; + + var obtainedJson = JsonConvert.SerializeObject(dictionary, jsonSerializerSettings); + + var obtainedDictionary = (Dictionary>)JsonConvert.DeserializeObject(obtainedJson, jsonSerializerSettings); + + Assert.IsNotNull(obtainedDictionary); + } + public class MailAddressReadConverter : JsonConverter { public override bool CanConvert(Type objectType) diff --git a/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs b/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs index 5df55dc2..2dfd7bdb 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs @@ -28,6 +28,7 @@ using System.Runtime.Serialization; using System.Reflection; using System.Globalization; using Newtonsoft.Json.Utilities; +using System.Collections.Generic; namespace Newtonsoft.Json.Serialization { @@ -38,9 +39,17 @@ namespace Newtonsoft.Json.Serialization { internal static readonly DefaultSerializationBinder Instance = new DefaultSerializationBinder(); - private readonly ThreadSafeStore _typeCache = new ThreadSafeStore(GetTypeFromTypeNameKey); + private readonly ThreadSafeStore _typeCache; - private static Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey) + /// + /// Initializes a new instance of the class. + /// + public DefaultSerializationBinder() + { + _typeCache = new ThreadSafeStore(GetTypeFromTypeNameKey); + } + + private Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey) { string assemblyName = typeNameKey.AssemblyName; string typeName = typeNameKey.TypeName; @@ -68,7 +77,8 @@ namespace Newtonsoft.Json.Serialization Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly a in loadedAssemblies) { - if (a.FullName == assemblyName) + // check for both full name or partial name match + if (a.FullName == assemblyName || a.GetName().Name == assemblyName) { assembly = a; break; @@ -85,7 +95,24 @@ namespace Newtonsoft.Json.Serialization Type type = assembly.GetType(typeName); if (type == null) { - throw new JsonSerializationException("Could not find type '{0}' in assembly '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeName, assembly.FullName)); + // if generic type, try manually parsing the type arguments for the case of dynamically loaded assemblies + // example generic typeName format: System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] + if (typeName.IndexOf('`') >= 0) + { + try + { + type = GetGenericTypeFromTypeName(typeName, assembly); + } + catch (Exception ex) + { + throw new JsonSerializationException("Could not find type '{0}' in assembly '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeName, assembly.FullName), ex); + } + } + + if (type == null) + { + throw new JsonSerializationException("Could not find type '{0}' in assembly '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeName, assembly.FullName)); + } } return type; @@ -96,6 +123,52 @@ namespace Newtonsoft.Json.Serialization } } + private Type GetGenericTypeFromTypeName(string typeName, Assembly assembly) + { + Type type = null; + var openBracketIndex = typeName.IndexOf('['); + if (openBracketIndex >= 0) + { + var genericTypeDefName = typeName.Substring(0, openBracketIndex); + var genericTypeDef = assembly.GetType(genericTypeDefName); + if (genericTypeDef != null) + { + var typeArgs = new List(); + var scope = 0; + var typeArgStartIndex = 0; + var endIndex = typeName.Length - 1; + for (var i = openBracketIndex + 1; i < endIndex; ++i) + { + var current = typeName[i]; + switch (current) + { + case '[': + if (scope == 0) + { + typeArgStartIndex = i + 1; + } + ++scope; + break; + case ']': + --scope; + if (scope == 0) + { + var typeArgAssemblyQualifiedName = typeName.Substring(typeArgStartIndex, i - typeArgStartIndex); + string typeArgTypeName; + string typeArgAssemblyName; + ReflectionUtils.SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName, out typeArgTypeName, out typeArgAssemblyName); + var typeArg = BindToType(typeArgAssemblyName, typeArgTypeName); + typeArgs.Add(typeArg); + } + break; + } + } + type = genericTypeDef.MakeGenericType(typeArgs.ToArray()); + } + } + return type; + } + internal struct TypeNameKey : IEquatable { internal readonly string AssemblyName; From a771eab395beb1e294a69360fd846f43803f3b50 Mon Sep 17 00:00:00 2001 From: TylerBrinkley Date: Mon, 8 Aug 2016 09:36:15 -0500 Subject: [PATCH 2/3] Fixed build on Net20 Tests. Didn't realize HashSet wasn't available in Net20. --- .../Newtonsoft.Json.Tests.Net35.csproj | 1 + .../Serialization/JsonSerializerTest.cs | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index a1f79940..ca6b7d07 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -374,6 +374,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 973b49dd..447f4344 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -9563,6 +9563,25 @@ Path '', line 1, position 1."); } #endif +#if !NET20 + [Test] + public void DeserializeDictionaryOfHashSetsWithTypeNameHandlingAll() + { + var dictionary = new Dictionary> + { + { 1, new HashSet(new[] { "test" }) }, + }; + + var jsonSerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; + + var obtainedJson = JsonConvert.SerializeObject(dictionary, jsonSerializerSettings); + + var obtainedDictionary = (Dictionary>)JsonConvert.DeserializeObject(obtainedJson, jsonSerializerSettings); + + Assert.IsNotNull(obtainedDictionary); + } +#endif + #if !(NETFX_CORE || DNXCORE50) [Test] public void MailMessageConverterTest() @@ -9618,23 +9637,6 @@ Path '', line 1, position 1."); "Cannot populate list type System.Net.Mime.HeaderCollection. Path 'Headers', line 26, position 14."); } - [Test] - public void DeserializeDictionaryOfHashSetsWithTypeNameHandlingAll() - { - var dictionary = new Dictionary> - { - { 1, new HashSet(new[] { "test" }) }, - }; - - var jsonSerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; - - var obtainedJson = JsonConvert.SerializeObject(dictionary, jsonSerializerSettings); - - var obtainedDictionary = (Dictionary>)JsonConvert.DeserializeObject(obtainedJson, jsonSerializerSettings); - - Assert.IsNotNull(obtainedDictionary); - } - public class MailAddressReadConverter : JsonConverter { public override bool CanConvert(Type objectType) From 9603e3fe7655de3c1c642309cf098da7da7c956e Mon Sep 17 00:00:00 2001 From: TylerBrinkley Date: Mon, 8 Aug 2016 10:15:42 -0500 Subject: [PATCH 3/3] Made test not available in PORTABLE or PORTABLE40. Fixed visual studio's auto update of Net35 Test project. --- Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj | 1 - Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index ca6b7d07..a1f79940 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -374,7 +374,6 @@ - diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 447f4344..69851375 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -9563,7 +9563,7 @@ Path '', line 1, position 1."); } #endif -#if !NET20 +#if !(NET20 || PORTABLE || PORTABLE40) [Test] public void DeserializeDictionaryOfHashSetsWithTypeNameHandlingAll() {