#region License // Copyright (c) 2007 James Newton-King // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #endregion using System; using System.Runtime.Serialization; using System.Reflection; using System.Globalization; using LC.Newtonsoft.Json.Utilities; using System.Collections.Generic; namespace LC.Newtonsoft.Json.Serialization { /// /// The default serialization binder used when resolving and loading classes from type names. /// public class DefaultSerializationBinder : #pragma warning disable 618 SerializationBinder, #pragma warning restore 618 ISerializationBinder { internal static readonly DefaultSerializationBinder Instance = new DefaultSerializationBinder(); private readonly ThreadSafeStore, Type> _typeCache; /// /// Initializes a new instance of the class. /// public DefaultSerializationBinder() { _typeCache = new ThreadSafeStore, Type>(GetTypeFromTypeNameKey); } private Type GetTypeFromTypeNameKey(StructMultiKey typeNameKey) { string? assemblyName = typeNameKey.Value1; string typeName = typeNameKey.Value2; if (assemblyName != null) { Assembly assembly; #if !(DOTNET || PORTABLE40 || PORTABLE) // look, I don't like using obsolete methods as much as you do but this is the only way // Assembly.Load won't check the GAC for a partial name #pragma warning disable 618,612 assembly = Assembly.LoadWithPartialName(assemblyName); #pragma warning restore 618,612 #elif DOTNET || PORTABLE assembly = Assembly.Load(new AssemblyName(assemblyName)); #else assembly = Assembly.Load(assemblyName); #endif #if HAVE_APP_DOMAIN if (assembly == null) { // will find assemblies loaded with Assembly.LoadFile outside of the main directory Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly a in loadedAssemblies) { // check for both full name or partial name match if (a.FullName == assemblyName || a.GetName().Name == assemblyName) { assembly = a; break; } } } #endif if (assembly == null) { throw new JsonSerializationException("Could not load assembly '{0}'.".FormatWith(CultureInfo.InvariantCulture, assemblyName)); } Type? type = assembly.GetType(typeName); if (type == null) { // 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; } else { return Type.GetType(typeName); } } 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); StructMultiKey typeNameKey = ReflectionUtils.SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName); genericTypeArguments.Add(GetTypeByName(typeNameKey)); } break; } } type = genericTypeDef.MakeGenericType(genericTypeArguments.ToArray()); } } return type; } private Type GetTypeByName(StructMultiKey typeNameKey) { return _typeCache.Get(typeNameKey); } /// /// When overridden in a derived class, controls the binding of a serialized object to a type. /// /// Specifies the name of the serialized object. /// Specifies the name of the serialized object. /// /// The type of the object the formatter creates a new instance of. /// public override Type BindToType(string? assemblyName, string typeName) { return GetTypeByName(new StructMultiKey(assemblyName, typeName)); } /// /// When overridden in a derived class, controls the binding of a serialized object to a type. /// /// The type of the object the formatter creates a new instance of. /// Specifies the name of the serialized object. /// Specifies the name of the serialized object. public #if HAVE_SERIALIZATION_BINDER_BIND_TO_NAME override #endif void BindToName(Type serializedType, out string? assemblyName, out string? typeName) { #if !HAVE_FULL_REFLECTION assemblyName = serializedType.GetTypeInfo().Assembly.FullName; typeName = serializedType.FullName; #else assemblyName = serializedType.Assembly.FullName; typeName = serializedType.FullName; #endif } } }