-Add support for using implicit/explicit casts when converting a JSON property name to a dictionary value

-Fix DynamicReflectionDelegateFactory erroring when calling static methods
-Fix CustomCreationConverter to return null when converting a null JSON value
This commit is contained in:
JamesNK
2010-03-23 00:21:57 +00:00
parent 408a407105
commit 7f7cccd598
7 changed files with 214 additions and 8 deletions
@@ -145,5 +145,86 @@ namespace Newtonsoft.Json.Tests.Converters
const string expected = @"{""Value"":""Foo"",""Thing"":{""Number"":456}}";
Assert.AreEqual(expected, json);
}
internal interface IRange<T>
{
T First { get; }
T Last { get; }
}
internal class Range<T> : IRange<T>
{
public T First { get; set; }
public T Last { get; set; }
}
internal class NullInterfaceTestClass
{
public virtual Guid Id { get; set; }
public virtual int? Year { get; set; }
public virtual string Company { get; set; }
public virtual IRange<decimal> DecimalRange { get; set; }
public virtual IRange<int> IntRange { get; set; }
public virtual IRange<decimal> NullDecimalRange { get; set; }
}
internal class DecimalRangeConverter : CustomCreationConverter<IRange<decimal>>
{
public override IRange<decimal> Create(Type objectType)
{
return new Range<decimal>();
}
}
internal class IntRangeConverter : CustomCreationConverter<IRange<int>>
{
public override IRange<int> Create(Type objectType)
{
return new Range<int>();
}
}
[Test]
public void DeserializeAndConvertNullValue()
{
NullInterfaceTestClass initial = new NullInterfaceTestClass
{
Company = "Company!",
DecimalRange = new Range<decimal> { First = 0, Last = 1 },
Id = new Guid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
IntRange = new Range<int> { First = int.MinValue, Last = int.MaxValue },
Year = 2010,
NullDecimalRange = null
};
string json = JsonConvert.SerializeObject(initial, Formatting.Indented);
Assert.AreEqual(@"{
""Id"": ""00000001-0002-0003-0405-060708090a0b"",
""Year"": 2010,
""Company"": ""Company!"",
""DecimalRange"": {
""First"": 0.0,
""Last"": 1.0
},
""IntRange"": {
""First"": -2147483648,
""Last"": 2147483647
},
""NullDecimalRange"": null
}", json);
NullInterfaceTestClass deserialized = JsonConvert.DeserializeObject<NullInterfaceTestClass>(
json, new IntRangeConverter(), new DecimalRangeConverter());
Assert.AreEqual("Company!", deserialized.Company);
Assert.AreEqual(new Guid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), deserialized.Id);
Assert.AreEqual(0, deserialized.DecimalRange.First);
Assert.AreEqual(1, deserialized.DecimalRange.Last);
Assert.AreEqual(int.MinValue, deserialized.IntRange.First);
Assert.AreEqual(int.MaxValue, deserialized.IntRange.Last);
Assert.AreEqual(null, deserialized.NullDecimalRange);
Assert.AreEqual(2010, deserialized.Year);
}
}
}
@@ -2000,7 +2000,7 @@ keyword such as type of business.""
}
[Test]
[ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Could not convert string 'Newtonsoft.Json.Tests.TestObjects.Person' to dictionary key type Newtonsoft.Json.Tests.TestObjects.Person. Create a TypeConverter to convert from the string to the key type object.")]
[ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Could not convert string 'Newtonsoft.Json.Tests.TestObjects.Person' to dictionary key type 'Newtonsoft.Json.Tests.TestObjects.Person'. Create a TypeConverter to convert from the string to the key type object.")]
public void DeserializePersonKeyedDictionary()
{
string json =
@@ -3316,5 +3316,41 @@ keyword such as type of business.""
Assert.AreEqual("James", deserialized.Name);
Assert.AreEqual(27, deserialized.Age);
}
public class DictionaryKey
{
public string Value { get; set; }
public override string ToString()
{
return Value;
}
public static implicit operator DictionaryKey(string value)
{
return new DictionaryKey() {Value = value};
}
}
[Test]
public void SerializeDeserializeDictionaryKey()
{
Dictionary<DictionaryKey, string> dictionary = new Dictionary<DictionaryKey, string>();
dictionary.Add(new DictionaryKey() { Value = "First!" }, "First");
dictionary.Add(new DictionaryKey() { Value = "Second!" }, "Second");
string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented);
Assert.AreEqual(@"{
""First!"": ""First"",
""Second!"": ""Second""
}", json);
Dictionary<DictionaryKey, string> newDictionary =
JsonConvert.DeserializeObject<Dictionary<DictionaryKey, string>>(json);
Assert.AreEqual(2, newDictionary.Count);
}
}
}
@@ -27,10 +27,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using NUnit.Framework;
using Newtonsoft.Json.Utilities;
using Newtonsoft.Json.Tests.TestObjects;
using Newtonsoft.Json.Tests.Serialization;
namespace Newtonsoft.Json.Tests.Utilities
{
@@ -89,6 +91,22 @@ namespace Newtonsoft.Json.Tests.Utilities
setter(m, new Version());
}
[Test]
public void CreateStaticMethodCall()
{
MethodInfo castMethodInfo = typeof(JsonSerializerTest.DictionaryKey).GetMethod("op_Implicit", new[] { typeof(string) });
Assert.IsNotNull(castMethodInfo);
MethodCall<object, object> call = DynamicReflectionDelegateFactory.Instance.CreateMethodCall<object>(castMethodInfo);
object result = call(null, "First!");
Assert.IsNotNull(result);
JsonSerializerTest.DictionaryKey key = (JsonSerializerTest.DictionaryKey) result;
Assert.AreEqual("First!", key.Value);
}
}
}
#endif
@@ -54,6 +54,9 @@ namespace Newtonsoft.Json.Converters
/// <returns>The object value.</returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
T value = Create(objectType);
if (value == null)
throw new JsonSerializationException("No object created.");
@@ -573,7 +573,7 @@ namespace Newtonsoft.Json.Serialization
}
catch (Exception ex)
{
throw new JsonSerializationException("Could not convert string '{0}' to dictionary key type {1}. Create a TypeConverter to convert from the string to the key type object.".FormatWith(CultureInfo.InvariantCulture, reader.Value, contract.DictionaryKeyType), ex);
throw new JsonSerializationException("Could not convert string '{0}' to dictionary key type '{1}'. Create a TypeConverter to convert from the string to the key type object.".FormatWith(CultureInfo.InvariantCulture, reader.Value, contract.DictionaryKeyType), ex);
}
CheckedRead(reader);
+73 -5
View File
@@ -30,6 +30,7 @@ using System.Text;
using System.Globalization;
using System.ComponentModel;
using Newtonsoft.Json.Serialization;
using System.Reflection;
#if !SILVERLIGHT
using System.Data.SqlTypes;
@@ -39,6 +40,63 @@ namespace Newtonsoft.Json.Utilities
{
internal static class ConvertUtils
{
internal struct TypeConvertKey : IEquatable<TypeConvertKey>
{
private readonly Type _initialType;
private readonly Type _targetType;
public Type InitialType
{
get { return _initialType; }
}
public Type TargetType
{
get { return _targetType; }
}
public TypeConvertKey(Type initialType, Type targetType)
{
_initialType = initialType;
_targetType = targetType;
}
public override int GetHashCode()
{
return _initialType.GetHashCode() ^ _targetType.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is TypeConvertKey))
return false;
return Equals((TypeConvertKey)obj);
}
public bool Equals(TypeConvertKey other)
{
return (_initialType == other._initialType && _targetType == other._targetType);
}
}
private static readonly ThreadSafeStore<TypeConvertKey, Func<object, object>> CastConverters =
new ThreadSafeStore<TypeConvertKey, Func<object, object>>(CreateCastConverter);
private static Func<object, object> CreateCastConverter(TypeConvertKey t)
{
MethodInfo castMethodInfo = t.TargetType.GetMethod("op_Implicit", new[] { t.InitialType });
if (castMethodInfo == null)
castMethodInfo = t.TargetType.GetMethod("op_Explicit", new[] { t.InitialType });
if (castMethodInfo == null)
return null;
MethodCall<object, object> call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(castMethodInfo);
return o => call(null, o);
}
public static bool CanConvertType(Type initialType, Type targetType, bool allowTypeNameToString)
{
ValidationUtils.ArgumentNotNull(initialType, "initialType");
@@ -387,12 +445,22 @@ namespace Newtonsoft.Json.Utilities
{
Type valueType = (value != null) ? value.GetType() : null;
if (value != null && targetType.IsAssignableFrom(valueType))
return value;
else if (value == null && ReflectionUtils.IsNullable(targetType))
return null;
if (value != null)
{
if (targetType.IsAssignableFrom(valueType))
return value;
Func<object, object> castConverter = CastConverters.Get(new TypeConvertKey(valueType, targetType));
if (castConverter != null)
return castConverter(value);
}
else
throw new Exception("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != null) ? initialType.ToString() : "{null}", targetType));
{
if (ReflectionUtils.IsNullable(targetType))
return null;
}
throw new Exception("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != null) ? initialType.ToString() : "{null}", targetType));
}
#if !SILVERLIGHT
@@ -66,7 +66,7 @@ namespace Newtonsoft.Json.Utilities
generator.MarkLabel(argsOk);
if (!method.IsConstructor)
if (!method.IsConstructor && !method.IsStatic)
generator.PushInstance(method.DeclaringType);
for (int i = 0; i < args.Length; i++)