diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 2e34257b..7a45e8e3 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -7878,6 +7878,41 @@ Parameter name: value", } #endif + public class ErroringJsonConverter : JsonConverter + { + public ErroringJsonConverter(string s) + { + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override bool CanConvert(Type objectType) + { + throw new NotImplementedException(); + } + } + + [JsonConverter(typeof(ErroringJsonConverter))] + public class ErroringTestClass + { + } + + [Test] + public void ErrorCreatingJsonConverter() + { + ExceptionAssert.Throws( + "Error creating 'Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+ErroringJsonConverter'.", + () => JsonConvert.SerializeObject(new ErroringTestClass())); + } + [Test] public void DeserializeInvalidOctalRootError() { diff --git a/Src/Newtonsoft.Json/JsonConverterAttribute.cs b/Src/Newtonsoft.Json/JsonConverterAttribute.cs index f1ab0d55..0b2453cd 100644 --- a/Src/Newtonsoft.Json/JsonConverterAttribute.cs +++ b/Src/Newtonsoft.Json/JsonConverterAttribute.cs @@ -57,17 +57,5 @@ namespace Newtonsoft.Json _converterType = converterType; } - - internal static JsonConverter CreateJsonConverterInstance(Type converterType) - { - try - { - return (JsonConverter)Activator.CreateInstance(converterType); - } - catch (Exception ex) - { - throw new JsonException("Error creating {0}".FormatWith(CultureInfo.InvariantCulture, converterType), ex); - } - } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index 50b1cb2d..2aa2606e 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -166,7 +166,7 @@ namespace Newtonsoft.Json.Schema private string GetTitle(Type type) { - JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type); + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(type); if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Title)) return containerAttribute.Title; @@ -176,7 +176,7 @@ namespace Newtonsoft.Json.Schema private string GetDescription(Type type) { - JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type); + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(type); if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Description)) return containerAttribute.Description; @@ -192,7 +192,7 @@ namespace Newtonsoft.Json.Schema private string GetTypeId(Type type, bool explicitOnly) { - JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type); + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(type); if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Id)) return containerAttribute.Id; @@ -278,7 +278,7 @@ namespace Newtonsoft.Json.Schema CurrentSchema.Id = GetTypeId(type, false); - JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetJsonContainerAttribute(type) as JsonArrayAttribute; + JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetCachedAttribute(type); bool allowNullItem = (arrayAttribute == null || arrayAttribute.AllowNullItems); Type collectionItemType = ReflectionUtils.GetCollectionItemType(type); diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index c05839d0..a383bf26 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -371,7 +371,7 @@ namespace Newtonsoft.Json.Serialization contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(contract.NonNullableUnderlyingType, ignoreSerializableAttribute); contract.Properties.AddRange(CreateProperties(contract.NonNullableUnderlyingType, contract.MemberSerialization)); - JsonObjectAttribute attribute = JsonTypeReflector.GetJsonObjectAttribute(contract.NonNullableUnderlyingType); + JsonObjectAttribute attribute = JsonTypeReflector.GetCachedAttribute(contract.NonNullableUnderlyingType); if (attribute != null) contract.ItemRequired = attribute._itemRequired; @@ -686,7 +686,7 @@ namespace Newtonsoft.Json.Serialization #endif private void InitializeContract(JsonContract contract) { - JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(contract.NonNullableUnderlyingType); + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(contract.NonNullableUnderlyingType); if (containerAttribute != null) { contract.IsReference = containerAttribute._isReference; @@ -963,18 +963,19 @@ namespace Newtonsoft.Json.Serialization /// A for the given type. protected virtual JsonContract CreateContract(Type objectType) { - Type t = ReflectionUtils.EnsureNotNullableType(objectType); - if (IsJsonPrimitiveType(objectType)) return CreatePrimitiveContract(objectType); - if (JsonTypeReflector.GetJsonObjectAttribute(t) != null) + Type t = ReflectionUtils.EnsureNotNullableType(objectType); + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(t); + + if (containerAttribute is JsonObjectAttribute) return CreateObjectContract(objectType); - if (JsonTypeReflector.GetJsonArrayAttribute(t) != null) + if (containerAttribute is JsonArrayAttribute) return CreateArrayContract(objectType); - if (JsonTypeReflector.GetJsonDictionaryAttribute(t) != null) + if (containerAttribute is JsonDictionaryAttribute) return CreateDictionaryContract(objectType); if (t == typeof(JToken) || t.IsSubclassOf(typeof(JToken))) @@ -1268,7 +1269,7 @@ namespace Newtonsoft.Json.Serialization property.ItemIsReference = (propertyAttribute != null) ? propertyAttribute._itemIsReference : null; property.ItemConverter = (propertyAttribute != null && propertyAttribute.ItemConverterType != null) - ? JsonConverterAttribute.CreateJsonConverterInstance(propertyAttribute.ItemConverterType) + ? JsonTypeReflector.CreateJsonConverterInstance(propertyAttribute.ItemConverterType) : null; property.ItemReferenceLoopHandling = (propertyAttribute != null) ? propertyAttribute._itemReferenceLoopHandling : null; property.ItemTypeNameHandling = (propertyAttribute != null) ? propertyAttribute._itemTypeNameHandling : null; diff --git a/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs index 6e47807d..0ff731cf 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs @@ -100,12 +100,12 @@ namespace Newtonsoft.Json.Serialization internal JsonContainerContract(Type underlyingType) : base(underlyingType) { - JsonContainerAttribute jsonContainerAttribute = JsonTypeReflector.GetJsonContainerAttribute(underlyingType); + JsonContainerAttribute jsonContainerAttribute = JsonTypeReflector.GetCachedAttribute(underlyingType); if (jsonContainerAttribute != null) { if (jsonContainerAttribute.ItemConverterType != null) - ItemConverter = JsonConverterAttribute.CreateJsonConverterInstance(jsonContainerAttribute.ItemConverterType); + ItemConverter = JsonTypeReflector.CreateJsonConverterInstance(jsonContainerAttribute.ItemConverterType); ItemIsReference = jsonContainerAttribute._itemIsReference; ItemReferenceLoopHandling = jsonContainerAttribute._itemReferenceLoopHandling; diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index 6cb642b6..bbe63876 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -55,7 +55,8 @@ namespace Newtonsoft.Json.Serialization public const string ShouldSerializePrefix = "ShouldSerialize"; public const string SpecifiedPostfix = "Specified"; - private static readonly ThreadSafeStore JsonConverterTypeCache = new ThreadSafeStore(GetJsonConverterTypeFromAttribute); + private static readonly ThreadSafeStore> JsonConverterCreatorCache = new ThreadSafeStore>(GetJsonConverterCreator); + #if !(NET20 || NETFX_CORE) private static readonly ThreadSafeStore AssociatedMetadataTypesCache = new ThreadSafeStore(GetAssociateMetadataTypeFromAttribute); @@ -65,33 +66,11 @@ namespace Newtonsoft.Json.Serialization private static Type _cachedMetadataTypeAttributeType; #endif - public static JsonContainerAttribute GetJsonContainerAttribute(Type type) + public static T GetCachedAttribute(object attributeProvider) where T : Attribute { - return CachedAttributeGetter.GetAttribute(type); + return CachedAttributeGetter.GetAttribute(attributeProvider); } - public static JsonObjectAttribute GetJsonObjectAttribute(Type type) - { - return GetJsonContainerAttribute(type) as JsonObjectAttribute; - } - - public static JsonArrayAttribute GetJsonArrayAttribute(Type type) - { - return GetJsonContainerAttribute(type) as JsonArrayAttribute; - } - - public static JsonDictionaryAttribute GetJsonDictionaryAttribute(Type type) - { - return GetJsonContainerAttribute(type) as JsonDictionaryAttribute; - } - -#if !(NETFX_CORE || PORTABLE || PORTABLE40) - public static SerializableAttribute GetSerializableAttribute(Type type) - { - return CachedAttributeGetter.GetAttribute(type); - } -#endif - #if !NET20 public static DataContractAttribute GetDataContractAttribute(Type type) { @@ -144,7 +123,7 @@ namespace Newtonsoft.Json.Serialization public static MemberSerialization GetObjectMemberSerialization(Type objectType, bool ignoreSerializableAttribute) { - JsonObjectAttribute objectAttribute = GetJsonObjectAttribute(objectType); + JsonObjectAttribute objectAttribute = GetCachedAttribute(objectType); if (objectAttribute != null) return objectAttribute.MemberSerialization; @@ -157,7 +136,7 @@ namespace Newtonsoft.Json.Serialization #if !(NETFX_CORE || PORTABLE40 || PORTABLE) if (!ignoreSerializableAttribute) { - SerializableAttribute serializableAttribute = GetSerializableAttribute(objectType); + SerializableAttribute serializableAttribute = GetCachedAttribute(objectType); if (serializableAttribute != null) return MemberSerialization.Fields; } @@ -167,33 +146,48 @@ namespace Newtonsoft.Json.Serialization return MemberSerialization.OptOut; } - private static Type GetJsonConverterType(object attributeProvider) - { - return JsonConverterTypeCache.Get(attributeProvider); - } - - private static Type GetJsonConverterTypeFromAttribute(object attributeProvider) - { - JsonConverterAttribute converterAttribute = GetAttribute(attributeProvider); - return (converterAttribute != null) - ? converterAttribute.ConverterType - : null; - } - public static JsonConverter GetJsonConverter(object attributeProvider) { - Type converterType = GetJsonConverterType(attributeProvider); + JsonConverterAttribute converterAttribute = GetCachedAttribute(attributeProvider); - if (converterType != null) + if (converterAttribute != null) { - JsonConverter memberConverter = JsonConverterAttribute.CreateJsonConverterInstance(converterType); - - return memberConverter; + Func creator = JsonConverterCreatorCache.Get(converterAttribute.ConverterType); + if (creator != null) + return creator(); } return null; } + public static JsonConverter CreateJsonConverterInstance(Type converterType) + { + Func converterCreator = JsonConverterCreatorCache.Get(converterType); + return converterCreator(); + } + + private static Func GetJsonConverterCreator(Type converterType) + { + Func defaultConstructor = (ReflectionUtils.HasDefaultConstructor(converterType, false)) + ? ReflectionDelegateFactory.CreateDefaultConstructor(converterType) + : null; + + return () => + { + try + { + if (defaultConstructor == null) + throw new JsonException("No parameterless constructor defined for '{0}'.".FormatWith(CultureInfo.InvariantCulture, converterType)); + + return (JsonConverter)defaultConstructor(); + } + catch (Exception ex) + { + throw new JsonException("Error creating '{0}'.".FormatWith(CultureInfo.InvariantCulture, converterType), ex); + } + }; + } + #if !(NETFX_CORE || PORTABLE40 || PORTABLE) public static TypeConverter GetTypeConverter(Type type) {