-Add support for sharing a static cache for contract resolvers inheriting from DefaultControlResolver

-Correct error message when casting non-JValue to a privative type
This commit is contained in:
JamesNK
2010-02-27 22:41:25 +00:00
parent d4158bb53c
commit 29b1ff5f0a
9 changed files with 204 additions and 33 deletions
@@ -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");
}
}
}
@@ -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;
}
}
}
@@ -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))
@@ -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]
@@ -11,6 +11,7 @@ namespace Newtonsoft.Json.Tests.Serialization
{
private readonly char _startingWithChar;
public DynamicContractResolver(char startingWithChar)
: base(false)
{
_startingWithChar = startingWithChar;
}
@@ -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<ClassWithTimeSpan>(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<string> GenericList { get; set; }
public GenericListTestClass()
{
GenericList = new List<string>();
}
}
[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<GenericListTestClass>(json);
Assert.AreEqual(2, newValue.GenericList.Count);
Assert.AreEqual(typeof(List<string>), newValue.GenericList.GetType());
}
}
}
+32 -27
View File
@@ -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
/// <summary>
/// Performs an explicit conversion from <see cref="Newtonsoft.Json.Linq.JToken"/> to <see cref="System.Boolean"/>.
@@ -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;
}
@@ -32,6 +32,14 @@ namespace Newtonsoft.Json.Serialization
/// </summary>
public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
/// <summary>
/// Initializes a new instance of the <see cref="CamelCasePropertyNamesContractResolver"/> class.
/// </summary>
public CamelCasePropertyNamesContractResolver()
: base(true)
{
}
/// <summary>
/// Resolves the name of the property.
/// </summary>
@@ -37,12 +37,42 @@ using Newtonsoft.Json.Linq;
namespace Newtonsoft.Json.Serialization
{
internal struct ResolverContractKey : IEquatable<ResolverContractKey>
{
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);
}
}
/// <summary>
/// Used by <see cref="JsonSerializer"/> to resolves a <see cref="JsonContract"/> for a given <see cref="Type"/>.
/// </summary>
public class DefaultContractResolver : IContractResolver
{
internal static readonly IContractResolver Instance = new DefaultContractResolver();
internal static readonly IContractResolver Instance = new DefaultContractResolver(true);
private static readonly IList<JsonConverter> BuiltInConverters = new List<JsonConverter>
{
#if !PocketPC && !SILVERLIGHT && !NET20
@@ -56,7 +86,11 @@ namespace Newtonsoft.Json.Serialization
#endif
};
private readonly ThreadSafeStore<Type, JsonContract> _typeContractCache;
private static Dictionary<ResolverContractKey, JsonContract> _sharedContractCache;
private static readonly object _typeContractCacheLock = new object();
private Dictionary<ResolverContractKey, JsonContract> _instanceContractCache;
private readonly bool _sharedCache;
/// <summary>
/// Gets or sets the default members search flags.
@@ -68,10 +102,37 @@ namespace Newtonsoft.Json.Serialization
/// Initializes a new instance of the <see cref="DefaultContractResolver"/> class.
/// </summary>
public DefaultContractResolver()
: this(false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DefaultContractResolver"/> class.
/// </summary>
/// <param name="shareCache">
/// if set to <c>true</c> the <see cref="IContractResolver"/> 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.
/// </param>
public DefaultContractResolver(bool shareCache)
{
DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance;
_sharedCache = shareCache;
}
_typeContractCache = new ThreadSafeStore<Type, JsonContract>(CreateContract);
private Dictionary<ResolverContractKey, JsonContract> GetCache()
{
if (_sharedCache)
return _sharedContractCache;
else
return _instanceContractCache;
}
private void UpdateCache(Dictionary<ResolverContractKey, JsonContract> cache)
{
if (_sharedCache)
_sharedContractCache = cache;
else
_instanceContractCache = cache;
}
/// <summary>
@@ -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<ResolverContractKey, JsonContract> 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<ResolverContractKey, JsonContract> updatedCache =
(cache != null)
? new Dictionary<ResolverContractKey, JsonContract>(cache)
: new Dictionary<ResolverContractKey, JsonContract>();
updatedCache[key] = contract;
UpdateCache(updatedCache);
}
}
return contract;
}
/// <summary>