diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 2d2d9797..69851375 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 || PORTABLE || PORTABLE40) + [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() diff --git a/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs b/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs index c18779c6..102968a4 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,57 @@ namespace Newtonsoft.Json.Serialization } } + private Type GetGenericTypeFromTypeName(string typeName, Assembly assembly) + { + Type type = null; + int openBracketIndex = typeName.IndexOf('['); + if (openBracketIndex >= 0) + { + string genericTypeDefName = typeName.Substring(0, openBracketIndex); + Type genericTypeDef = assembly.GetType(genericTypeDefName); + if (genericTypeDef != null) + { + List genericTypeArguments = new List(); + int scope = 0; + int typeArgStartIndex = 0; + int endIndex = typeName.Length - 1; + for (int i = openBracketIndex + 1; i < endIndex; ++i) + { + char current = typeName[i]; + switch (current) + { + case '[': + if (scope == 0) + { + typeArgStartIndex = i + 1; + } + ++scope; + break; + case ']': + --scope; + if (scope == 0) + { + string typeArgAssemblyQualifiedName = typeName.Substring(typeArgStartIndex, i - typeArgStartIndex); + + TypeNameKey typeNameKey = ReflectionUtils.SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName); + genericTypeArguments.Add(GetTypeByName(typeNameKey)); + } + break; + } + } + + type = genericTypeDef.MakeGenericType(genericTypeArguments.ToArray()); + } + } + + return type; + } + + private Type GetTypeByName(TypeNameKey typeNameKey) + { + return _typeCache.Get(typeNameKey); + } + /// /// When overridden in a derived class, controls the binding of a serialized object to a type. /// @@ -106,7 +184,7 @@ namespace Newtonsoft.Json.Serialization /// public override Type BindToType(string assemblyName, string typeName) { - return _typeCache.Get(new TypeNameKey(assemblyName, typeName)); + return GetTypeByName(new TypeNameKey(assemblyName, typeName)); } #if !(NET35 || NET20)