diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index c161ea73..2f5ba518 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -1449,5 +1449,35 @@ Parameter name: arrayIndex")] ""title"": null }", output); } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not convert Object to String.")] + public void InvalidValueCastExceptionMessage() + { + string json = @"{ + ""responseData"": {}, + ""responseDetails"": null, + ""responseStatus"": 200 +}"; + + JObject o = JObject.Parse(json); + + string name = (string)o["responseData"]; + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not convert Object to String.")] + public void InvalidPropertyValueCastExceptionMessage() + { + string json = @"{ + ""responseData"": {}, + ""responseDetails"": null, + ""responseStatus"": 200 +}"; + + JObject o = JObject.Parse(json); + + string name = (string)o.Property("responseData"); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index a7c5f58d..f2798e2c 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -178,5 +178,13 @@ namespace Newtonsoft.Json.Tests.Linq JToken t = new JValue(5L); t[0] = new JValue(3); } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not convert Null to Int32.")] + public void CastNullValueToNonNullable() + { + JValue v = new JValue((object)null); + int i = (int) v; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index f907f017..db5ced4c 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -333,6 +333,11 @@ namespace Newtonsoft.Json.Tests.Schema public class CustomDirectoryInfoMapper : DefaultContractResolver { + public CustomDirectoryInfoMapper() + : base(true) + { + } + protected override JsonContract CreateContract(Type objectType) { if (objectType == typeof(DirectoryInfo)) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs index 67fad8d0..5e31d4b5 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs @@ -65,6 +65,14 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(person.BirthDate, deserializedPerson.BirthDate); Assert.AreEqual(person.LastModified, deserializedPerson.LastModified); Assert.AreEqual(person.Name, deserializedPerson.Name); + + json = JsonConvert.SerializeObject(person, Formatting.Indented); + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""BirthDate"": ""\/Date(974764544000)\/"", + ""LastModified"": ""\/Date(974764544000)\/"" +}", json); + } [Test] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs index 91721aff..754f941a 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -11,6 +11,7 @@ namespace Newtonsoft.Json.Tests.Serialization { private readonly char _startingWithChar; public DynamicContractResolver(char startingWithChar) + : base(false) { _startingWithChar = startingWithChar; } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index e1f0c065..1cde02d3 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -689,11 +689,11 @@ keyword such as type of business."" [Test] public void TimeSpanTest() { - TimeSpan ts = new TimeSpan(200, 23, 59, 1); + TimeSpan ts = new TimeSpan(00, 23, 59, 1); string json = JsonConvert.SerializeObject(new ClassWithTimeSpan { TimeSpanField = ts }, Formatting.Indented); Assert.AreEqual(@"{ - ""TimeSpanField"": ""200.23:59:01"" + ""TimeSpanField"": ""23:59:01"" }", json); ClassWithTimeSpan c = JsonConvert.DeserializeObject(json); @@ -3032,5 +3032,29 @@ keyword such as type of business."" d2.Add(s, 5); Assert.AreEqual(@"{""host\\user"":5}", JsonConvert.SerializeObject(d2)); } + + public class GenericListTestClass + { + public List GenericList { get; set; } + + public GenericListTestClass() + { + GenericList = new List(); + } + } + + [Test] + public void DeserializeExistingGenericList() + { + GenericListTestClass c = new GenericListTestClass(); + c.GenericList.Add("1"); + c.GenericList.Add("2"); + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + GenericListTestClass newValue = JsonConvert.DeserializeObject(json); + Assert.AreEqual(2, newValue.GenericList.Count); + Assert.AreEqual(typeof(List), newValue.GenericList.GetType()); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index d9d50e33..3d6a027c 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -370,6 +370,16 @@ namespace Newtonsoft.Json.Linq return v; } + private static string GetType(JToken token) + { + ValidationUtils.ArgumentNotNull(token, "token"); + + if (token is JProperty) + token = ((JProperty)token).Value; + + return token.Type.ToString(); + } + private static bool IsNullable(JToken o) { return (o.Type == JTokenType.Undefined || o.Type == JTokenType.Null); @@ -405,11 +415,6 @@ namespace Newtonsoft.Json.Linq return (o.Type == JTokenType.Bytes || IsNullable(o)); } - private static string GetType(JToken t) - { - return (t != null) ? t.Type.ToString() : "{null}"; - } - #region Cast from operators /// /// Performs an explicit conversion from to . @@ -420,7 +425,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateBoolean(v, false)) - throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (bool)v.Value; } @@ -435,7 +440,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateDate(v, false)) - throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (DateTimeOffset)v.Value; } @@ -453,7 +458,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateBoolean(v, true)) - throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (bool?)v.Value; } @@ -467,7 +472,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, false)) - throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (long)v.Value; } @@ -484,7 +489,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateDate(v, true)) - throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (DateTime?)v.Value; } @@ -502,7 +507,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateDate(v, true)) - throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (DateTimeOffset?)v.Value; } @@ -520,7 +525,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateFloat(v, true)) - throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (v.Value != null) ? (decimal?)Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture) : null; } @@ -537,7 +542,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateFloat(v, true)) - throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (double?)v.Value; } @@ -551,7 +556,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, false)) - throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return Convert.ToInt32(v.Value, CultureInfo.InvariantCulture); } @@ -568,7 +573,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, true)) - throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (v.Value != null) ? (int?)Convert.ToInt32(v.Value, CultureInfo.InvariantCulture) : null; } @@ -582,7 +587,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateDate(v, false)) - throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (DateTime)v.Value; } @@ -599,7 +604,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, true)) - throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (long?)v.Value; } @@ -616,7 +621,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateFloat(v, true)) - throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (v.Value != null) ? (float?)Convert.ToSingle(v.Value, CultureInfo.InvariantCulture) : null; } @@ -630,7 +635,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateFloat(v, false)) - throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture); } @@ -647,7 +652,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, true)) - throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (uint?)v.Value; } @@ -664,7 +669,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, true)) - throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (ulong?)v.Value; } @@ -678,7 +683,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateFloat(v, false)) - throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (double)v.Value; } @@ -692,7 +697,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateFloat(v, false)) - throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return Convert.ToSingle(v.Value, CultureInfo.InvariantCulture); } @@ -709,7 +714,7 @@ namespace Newtonsoft.Json.Linq JValue v = EnsureValue(value); if (v == null || !ValidateString(v)) - throw new ArgumentException("Can not convert {0} to String.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to String.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (string)v.Value; } @@ -723,7 +728,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, false)) - throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return Convert.ToUInt32(v.Value, CultureInfo.InvariantCulture); } @@ -737,7 +742,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateInteger(v, false)) - throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return Convert.ToUInt64(v.Value, CultureInfo.InvariantCulture); } @@ -751,7 +756,7 @@ namespace Newtonsoft.Json.Linq { JValue v = EnsureValue(value); if (v == null || !ValidateBytes(v)) - throw new ArgumentException("Can not convert {0} to byte array.".FormatWith(CultureInfo.InvariantCulture, GetType(v))); + throw new ArgumentException("Can not convert {0} to byte array.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); return (byte[])v.Value; } diff --git a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs index a723b8e9..acda2435 100644 --- a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs @@ -32,6 +32,14 @@ namespace Newtonsoft.Json.Serialization /// public class CamelCasePropertyNamesContractResolver : DefaultContractResolver { + /// + /// Initializes a new instance of the class. + /// + public CamelCasePropertyNamesContractResolver() + : base(true) + { + } + /// /// Resolves the name of the property. /// diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 1d344ad4..03097ee2 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -37,12 +37,42 @@ using Newtonsoft.Json.Linq; namespace Newtonsoft.Json.Serialization { + internal struct ResolverContractKey : IEquatable + { + private readonly Type _resolverType; + private readonly Type _contractType; + + public ResolverContractKey(Type resolverType, Type contractType) + { + _resolverType = resolverType; + _contractType = contractType; + } + + public override int GetHashCode() + { + return _resolverType.GetHashCode() ^ _contractType.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is ResolverContractKey)) + return false; + + return Equals((ResolverContractKey) obj); + } + + public bool Equals(ResolverContractKey other) + { + return (_resolverType == other._resolverType && _contractType == other._contractType); + } + } + /// /// Used by to resolves a for a given . /// public class DefaultContractResolver : IContractResolver { - internal static readonly IContractResolver Instance = new DefaultContractResolver(); + internal static readonly IContractResolver Instance = new DefaultContractResolver(true); private static readonly IList BuiltInConverters = new List { #if !PocketPC && !SILVERLIGHT && !NET20 @@ -56,7 +86,11 @@ namespace Newtonsoft.Json.Serialization #endif }; - private readonly ThreadSafeStore _typeContractCache; + private static Dictionary _sharedContractCache; + private static readonly object _typeContractCacheLock = new object(); + + private Dictionary _instanceContractCache; + private readonly bool _sharedCache; /// /// Gets or sets the default members search flags. @@ -68,10 +102,37 @@ namespace Newtonsoft.Json.Serialization /// Initializes a new instance of the class. /// public DefaultContractResolver() + : this(false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// if set to true the will use a cached shared with other resolvers of the same type. + /// This will improve significantly performance for unshared resolvers but cause unexpected behavior if instances of the resolver could produce different results. + /// + public DefaultContractResolver(bool shareCache) { DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance; + _sharedCache = shareCache; + } - _typeContractCache = new ThreadSafeStore(CreateContract); + private Dictionary GetCache() + { + if (_sharedCache) + return _sharedContractCache; + else + return _instanceContractCache; + } + + private void UpdateCache(Dictionary cache) + { + if (_sharedCache) + _sharedContractCache = cache; + else + _instanceContractCache = cache; } /// @@ -84,7 +145,28 @@ namespace Newtonsoft.Json.Serialization if (type == null) throw new ArgumentNullException("type"); - return _typeContractCache.Get(type); + JsonContract contract; + ResolverContractKey key = new ResolverContractKey(GetType(), type); + Dictionary cache = GetCache(); + if (cache == null || !cache.TryGetValue(key, out contract)) + { + contract = CreateContract(type); + + // avoid the possibility of modifying the cache dictionary while another thread is accessing it + lock (_typeContractCacheLock) + { + cache = GetCache(); + Dictionary updatedCache = + (cache != null) + ? new Dictionary(cache) + : new Dictionary(); + updatedCache[key] = contract; + + UpdateCache(updatedCache); + } + } + + return contract; } ///