From 4605894b6b3c34859baa0561135e75b5ff8895c5 Mon Sep 17 00:00:00 2001 From: TylerBrinkley Date: Mon, 8 Aug 2016 09:18:34 -0500 Subject: [PATCH] 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;