diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 3a2e9352..b0bff76e 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -235,6 +235,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs index ce2e34ad..5dd5c424 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -108,6 +108,155 @@ namespace Newtonsoft.Json.Tests.Serialization [TestFixture] public class ContractResolverTests : TestFixtureBase { + [Test] + public void ListInterface() + { + var resolver = new DefaultContractResolver(); + var contract = (JsonArrayContract)resolver.ResolveContract(typeof(IList)); + + Assert.IsTrue(contract.IsInstantiable); + Assert.AreEqual(typeof(List), contract.CreatedType); + Assert.IsNotNull(contract.DefaultCreator); + } + + [Test] + public void AbstractTestClass() + { + var resolver = new DefaultContractResolver(); + var contract = (JsonObjectContract)resolver.ResolveContract(typeof(AbstractTestClass)); + + Assert.IsFalse(contract.IsInstantiable); + Assert.IsNull(contract.DefaultCreator); + Assert.IsNull(contract.OverrideCreator); + + ExceptionAssert.Throws( + "Could not create an instance of type Newtonsoft.Json.Tests.Serialization.AbstractTestClass. Type is an interface or abstract class and cannot be instantiated. Path 'Value', line 1, position 7.", + () => JsonConvert.DeserializeObject(@"{Value:'Value!'}", new JsonSerializerSettings + { + ContractResolver = resolver + })); + + contract.DefaultCreator = () => new AbstractImplementationTestClass(); + + var o = JsonConvert.DeserializeObject(@"{Value:'Value!'}", new JsonSerializerSettings + { + ContractResolver = resolver + }); + + Assert.AreEqual("Value!", o.Value); + } + + [Test] + public void AbstractListTestClass() + { + var resolver = new DefaultContractResolver(); + var contract = (JsonArrayContract)resolver.ResolveContract(typeof(AbstractListTestClass)); + + Assert.IsFalse(contract.IsInstantiable); + Assert.IsNull(contract.DefaultCreator); + Assert.IsNull(contract.ParametrizedCreator); + + ExceptionAssert.Throws( + "Could not create an instance of type Newtonsoft.Json.Tests.Serialization.AbstractListTestClass`1[System.Int32]. Type is an interface or abstract class and cannot be instantiated. Path '', line 1, position 1.", + () => JsonConvert.DeserializeObject>(@"[1,2]", new JsonSerializerSettings + { + ContractResolver = resolver + })); + + contract.DefaultCreator = () => new AbstractImplementationListTestClass(); + + var l = JsonConvert.DeserializeObject>(@"[1,2]", new JsonSerializerSettings + { + ContractResolver = resolver + }); + + Assert.AreEqual(2, l.Count); + Assert.AreEqual(1, l[0]); + Assert.AreEqual(2, l[1]); + } + + public class CustomList : List + { + } + + [Test] + public void ListInterfaceDefaultCreator() + { + var resolver = new DefaultContractResolver(); + var contract = (JsonArrayContract)resolver.ResolveContract(typeof(IList)); + + Assert.IsTrue(contract.IsInstantiable); + Assert.IsNotNull(contract.DefaultCreator); + + contract.DefaultCreator = () => new CustomList(); + + var l = JsonConvert.DeserializeObject>(@"[1,2,3]", new JsonSerializerSettings + { + ContractResolver = resolver + }); + + Assert.AreEqual(typeof(CustomList), l.GetType()); + Assert.AreEqual(3, l.Count); + Assert.AreEqual(1, l[0]); + Assert.AreEqual(2, l[1]); + Assert.AreEqual(3, l[2]); + } + + public class CustomDictionary : Dictionary + { + } + + [Test] + public void DictionaryInterfaceDefaultCreator() + { + var resolver = new DefaultContractResolver(); + var contract = (JsonDictionaryContract)resolver.ResolveContract(typeof(IDictionary)); + + Assert.IsTrue(contract.IsInstantiable); + Assert.IsNotNull(contract.DefaultCreator); + + contract.DefaultCreator = () => new CustomDictionary(); + + var d = JsonConvert.DeserializeObject>(@"{key1:1,key2:2}", new JsonSerializerSettings + { + ContractResolver = resolver + }); + + Assert.AreEqual(typeof(CustomDictionary), d.GetType()); + Assert.AreEqual(2, d.Count); + Assert.AreEqual(1, d["key1"]); + Assert.AreEqual(2, d["key2"]); + } + + [Test] + public void AbstractDictionaryTestClass() + { + var resolver = new DefaultContractResolver(); + var contract = (JsonDictionaryContract)resolver.ResolveContract(typeof(AbstractDictionaryTestClass)); + + Assert.IsFalse(contract.IsInstantiable); + Assert.IsNull(contract.DefaultCreator); + Assert.IsNull(contract.ParametrizedCreator); + + ExceptionAssert.Throws( + "Could not create an instance of type Newtonsoft.Json.Tests.Serialization.AbstractDictionaryTestClass`2[System.String,System.Int32]. Type is an interface or abstract class and cannot be instantiated. Path 'key1', line 1, position 6.", + () => JsonConvert.DeserializeObject>(@"{key1:1,key2:2}", new JsonSerializerSettings + { + ContractResolver = resolver + })); + + contract.DefaultCreator = () => new AbstractImplementationDictionaryTestClass(); + + var d = JsonConvert.DeserializeObject>(@"{key1:1,key2:2}", new JsonSerializerSettings + { + ContractResolver = resolver + }); + + Assert.AreEqual(2, d.Count); + Assert.AreEqual(1, d["key1"]); + Assert.AreEqual(2, d["key2"]); + } + [Test] public void SerializeWithEscapedPropertyName() { diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DynamicConcreteTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DynamicConcreteTests.cs new file mode 100644 index 00000000..adcdf329 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Serialization/DynamicConcreteTests.cs @@ -0,0 +1,281 @@ +#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.Reflection; +using System.Reflection.Emit; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Tests.TestObjects; +using System; +using System.Linq; + +#if !NETFX_CORE +using NUnit.Framework; +#else +using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; +using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute; +using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute; +#endif + +namespace Newtonsoft.Json.Tests.Serialization +{ + [TestFixture] + public class DynamicConcreteTests : TestFixtureBase + { + [Test] + public void UseDynamicConcreteIfTargetObjectTypeIsAnInterfaceWithNoBackingClass() + { + string json = @"{Name:""Name!""}"; + + var c = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + ContractResolver = new DynamicConcreteContractResolver() + }); + + Assert.AreEqual("Name!", c.Name); + } + + [Test] + public void UseDynamicConcreteIfTargetObjectTypeIsAnAbstractClassWithNoConcrete() + { + string json = @"{Name:""Name!"", Game:""Same""}"; + + var c = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + ContractResolver = new DynamicConcreteContractResolver() + }); + + Assert.AreEqual("Name!", c.Name); + Assert.AreEqual("Same", c.Game); + } + + [Test] + public void AnyMethodsExposedByDynamicConcreteAreHarmless() + { + string json = @"{Name:""Name!""}"; + + var c = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + ContractResolver = new DynamicConcreteContractResolver() + }); + + c.FuncWithRefType(10, null); + c.FuncWithValType_1(); + c.FuncWithValType_2(); + } + } + + public abstract class AbstractWithNoConcrete + { + public string Name { get; set; } + public abstract string Game { get; set; } + } + + public interface IInterfaceWithNoConcrete + { + string Name { get; set; } + object FuncWithRefType(int a, object b); + int FuncWithValType_1(); + bool FuncWithValType_2(); + } + + public class DynamicConcreteContractResolver : DefaultContractResolver + { + protected override JsonContract CreateContract(Type objectType) + { + JsonContract contract = base.CreateContract(objectType); + if (contract.CreatedType.IsAbstract || contract.CreatedType.IsInterface) + contract.DefaultCreator = () => DynamicConcrete.GetInstanceFor(contract.CreatedType); + + return contract; + } + } + + /// + /// Creates run-time backing types for abstract classes and interfaces + /// + public static class DynamicConcrete + { + /// + /// Get an empty instance of a dynamic proxy for type T. + /// All public fields are writable and all properties have both getters and setters. + /// + public static T GetInstanceFor() + { + return (T)GetInstanceFor(typeof(T)); + } + + static readonly ModuleBuilder ModuleBuilder; + static readonly AssemblyBuilder DynamicAssembly; + + /// + /// Get an empty instance of a dynamic proxy for the given type. + /// All public fields are writable and all properties have both getters and setters. + /// + public static object GetInstanceFor(Type targetType) + { + lock (DynamicAssembly) + { + var constructedType = DynamicAssembly.GetType(ProxyName(targetType)) ?? GetConstructedType(targetType); + var instance = Activator.CreateInstance(constructedType); + return instance; + } + } + + static string ProxyName(Type targetType) + { + return targetType.Name + "Proxy"; + } + + static DynamicConcrete() + { + var assemblyName = new AssemblyName("DynImpl"); + DynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); + ModuleBuilder = DynamicAssembly.DefineDynamicModule("DynImplModule"); + } + + static Type GetConstructedType(Type targetType) + { + var typeBuilder = ModuleBuilder.DefineType(targetType.Name + "Proxy", TypeAttributes.Public); + + var ctorBuilder = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + new Type[] { }); + var ilGenerator = ctorBuilder.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ret); + + IncludeType(targetType, typeBuilder); + + foreach (var face in targetType.GetInterfaces()) + IncludeType(face, typeBuilder); + + return typeBuilder.CreateType(); + } + + static void IncludeType(Type typeOfT, TypeBuilder typeBuilder) + { + var methodInfos = typeOfT.GetMethods(); + foreach (var methodInfo in methodInfos) + { + if (methodInfo.Name.StartsWith("set_")) continue; // we always add a set for a get. + + if (methodInfo.Name.StartsWith("get_")) + { + BindProperty(typeBuilder, methodInfo); + } + else + { + if (methodInfo.IsAbstract) BindMethod(typeBuilder, methodInfo); + } + } + + if (typeOfT.IsInterface) typeBuilder.AddInterfaceImplementation(typeOfT); + else if (typeOfT.IsAbstract) typeBuilder.SetParent(typeOfT); + } + + static void BindMethod(TypeBuilder typeBuilder, MethodInfo methodInfo) + { + var args = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray(); + var methodBuilder = typeBuilder.DefineMethod( + methodInfo.Name, + MethodAttributes.Public | MethodAttributes.Virtual, + CallingConventions.HasThis, + methodInfo.ReturnType, + args + ); + + var methodILGen = methodBuilder.GetILGenerator(); + if (methodInfo.ReturnType == typeof(void)) + { + methodILGen.Emit(OpCodes.Ret); + } + else + { + if (methodInfo.ReturnType.IsPrimitive) + { + methodILGen.Emit(OpCodes.Ldc_I4_0); + } + else if (methodInfo.ReturnType.IsValueType || methodInfo.ReturnType.IsEnum) + { + var getMethod = typeof(Activator).GetMethod("CreateInstance", + new[] { typeof(Type) }); + var lb = methodILGen.DeclareLocal(methodInfo.ReturnType); + if (lb.LocalType != null) + { + methodILGen.Emit(OpCodes.Ldtoken, lb.LocalType); + methodILGen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); + methodILGen.Emit(OpCodes.Callvirt, getMethod); + methodILGen.Emit(OpCodes.Unbox_Any, lb.LocalType); + } + } + else + { + methodILGen.Emit(OpCodes.Ldnull); + } + methodILGen.Emit(OpCodes.Ret); + } + typeBuilder.DefineMethodOverride(methodBuilder, methodInfo); + } + + /// + /// Bind a new property into a type builder with getters and setters. + /// + public static void BindProperty(TypeBuilder typeBuilder, MethodInfo methodInfo) + { + // Backing Field + var propertyName = methodInfo.Name.Replace("get_", ""); + var propertyType = methodInfo.ReturnType; + var backingField = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); + + //Getter + var backingGet = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | + MethodAttributes.SpecialName | MethodAttributes.Virtual | + MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); + var getIl = backingGet.GetILGenerator(); + + getIl.Emit(OpCodes.Ldarg_0); + getIl.Emit(OpCodes.Ldfld, backingField); + getIl.Emit(OpCodes.Ret); + + + //Setter + var backingSet = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | + MethodAttributes.SpecialName | MethodAttributes.Virtual | + MethodAttributes.HideBySig, null, new[] { propertyType }); + + var setIl = backingSet.GetILGenerator(); + + setIl.Emit(OpCodes.Ldarg_0); + setIl.Emit(OpCodes.Ldarg_1); + setIl.Emit(OpCodes.Stfld, backingField); + setIl.Emit(OpCodes.Ret); + + // Property + var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null); + propertyBuilder.SetGetMethod(backingGet); + propertyBuilder.SetSetMethod(backingSet); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 5c504b09..eddf7607 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -7898,6 +7898,31 @@ Parameter name: value", } } + public abstract class AbstractTestClass + { + public string Value { get; set; } + } + + public class AbstractImplementationTestClass : AbstractTestClass + { + } + + public abstract class AbstractListTestClass : List + { + } + + public class AbstractImplementationListTestClass : AbstractListTestClass + { + } + + public abstract class AbstractDictionaryTestClass : Dictionary + { + } + + public class AbstractImplementationDictionaryTestClass : AbstractDictionaryTestClass + { + } + public class PublicConstructorOverridenByJsonConstructor { public string Value { get; private set; } diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index ff88ee30..245a295c 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -375,34 +375,37 @@ namespace Newtonsoft.Json.Serialization if (attribute != null) contract.ItemRequired = attribute._itemRequired; - ConstructorInfo overrideConstructor = GetAttributeConstructor(contract.NonNullableUnderlyingType); + if (contract.IsInstantiable) + { + ConstructorInfo overrideConstructor = GetAttributeConstructor(contract.NonNullableUnderlyingType); - // check if a JsonConstructorAttribute has been defined and use that - if (overrideConstructor != null) - { -#pragma warning disable 618 - contract.OverrideConstructor = overrideConstructor; -#pragma warning restore 618 - contract.CreatorParameters.AddRange(CreateConstructorParameters(overrideConstructor, contract.Properties)); - } - else if (contract.MemberSerialization == MemberSerialization.Fields) - { -#if !(NETFX_CORE || PORTABLE40 || PORTABLE) - // mimic DataContractSerializer behaviour when populating fields by overriding default creator to create an uninitialized object - // note that this is only possible when the application is fully trusted so fall back to using the default constructor (if available) in partial trust - if (JsonTypeReflector.FullyTrusted) - contract.DefaultCreator = contract.GetUninitializedObject; -#endif - } - else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic) - { - ConstructorInfo constructor = GetParametrizedConstructor(contract.NonNullableUnderlyingType); - if (constructor != null) + // check if a JsonConstructorAttribute has been defined and use that + if (overrideConstructor != null) { #pragma warning disable 618 - contract.ParametrizedConstructor = constructor; + contract.OverrideConstructor = overrideConstructor; #pragma warning restore 618 - contract.CreatorParameters.AddRange(CreateConstructorParameters(constructor, contract.Properties)); + contract.CreatorParameters.AddRange(CreateConstructorParameters(overrideConstructor, contract.Properties)); + } + else if (contract.MemberSerialization == MemberSerialization.Fields) + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + // mimic DataContractSerializer behaviour when populating fields by overriding default creator to create an uninitialized object + // note that this is only possible when the application is fully trusted so fall back to using the default constructor (if available) in partial trust + if (JsonTypeReflector.FullyTrusted) + contract.DefaultCreator = contract.GetUninitializedObject; +#endif + } + else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic) + { + ConstructorInfo constructor = GetParametrizedConstructor(contract.NonNullableUnderlyingType); + if (constructor != null) + { +#pragma warning disable 618 + contract.ParametrizedConstructor = constructor; +#pragma warning restore 618 + contract.CreatorParameters.AddRange(CreateConstructorParameters(constructor, contract.Properties)); + } } } @@ -703,8 +706,8 @@ namespace Newtonsoft.Json.Serialization // then see whether object is compadible with any of the built in converters contract.InternalConverter = JsonSerializer.GetMatchingConverter(BuiltInConverters, contract.NonNullableUnderlyingType); - if (ReflectionUtils.HasDefaultConstructor(contract.CreatedType, true) - || contract.CreatedType.IsValueType()) + if (contract.IsInstantiable + && (ReflectionUtils.HasDefaultConstructor(contract.CreatedType, true) || contract.CreatedType.IsValueType())) { contract.DefaultCreator = GetDefaultCreator(contract.CreatedType); diff --git a/Src/Newtonsoft.Json/Serialization/JsonContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContract.cs index b1cf0a78..dfc8a19b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContract.cs @@ -86,12 +86,12 @@ namespace Newtonsoft.Json.Serialization { internal bool IsNullable; internal bool IsConvertable; - internal bool IsSealed; internal bool IsEnum; internal Type NonNullableUnderlyingType; internal ReadType InternalReadType; internal JsonContractType ContractType; internal bool IsReadOnlyOrFixedSize; + internal bool IsSealed; internal bool IsInstantiable; private List _onDeserializedCallbacks; @@ -99,6 +99,7 @@ namespace Newtonsoft.Json.Serialization private IList _onSerializedCallbacks; private IList _onSerializingCallbacks; private IList _onErrorCallbacks; + private Type _createdType; /// /// Gets the underlying type for the contract. @@ -110,7 +111,17 @@ namespace Newtonsoft.Json.Serialization /// Gets or sets the type created during deserialization. /// /// The type created during deserialization. - public Type CreatedType { get; set; } + public Type CreatedType + { + get { return _createdType; } + set + { + _createdType = value; + + IsSealed = _createdType.IsSealed(); + IsInstantiable = !(_createdType.IsInterface() || _createdType.IsAbstract()); + } + } /// /// Gets or sets whether this type contract is serialized as a reference. @@ -296,9 +307,6 @@ namespace Newtonsoft.Json.Serialization UnderlyingType = underlyingType; - IsSealed = underlyingType.IsSealed(); - IsInstantiable = !(underlyingType.IsInterface() || underlyingType.IsAbstract()); - IsNullable = ReflectionUtils.IsNullable(underlyingType); NonNullableUnderlyingType = (IsNullable && ReflectionUtils.IsNullableType(underlyingType)) ? Nullable.GetUnderlyingType(underlyingType) : underlyingType; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index ca275474..27a2d315 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1036,6 +1036,9 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } else { + if (!contract.IsInstantiable) + throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + throw JsonSerializationException.Create(reader, "Unable to find a constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); } } @@ -1064,6 +1067,9 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } else { + if (!contract.IsInstantiable) + throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + throw JsonSerializationException.Create(reader, "Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); } } @@ -1825,9 +1831,6 @@ To fix this error either change the environment to be fully trusted, change the { object newObject = null; - if (!objectContract.IsInstantiable) - throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, objectContract.UnderlyingType)); - if (objectContract.OverrideCreator != null) { if (objectContract.CreatorParameters.Count > 0) @@ -1854,7 +1857,12 @@ To fix this error either change the environment to be fully trusted, change the } if (newObject == null) + { + if (!objectContract.IsInstantiable) + throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, objectContract.UnderlyingType)); + throw JsonSerializationException.Create(reader, "Unable to find a constructor to use for type {0}. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.".FormatWith(CultureInfo.InvariantCulture, objectContract.UnderlyingType)); + } createdFromNonDefaultCreator = false; return newObject;