-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:
@@ -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);
|
||||
|
||||
@@ -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++)
|
||||
|
||||
Reference in New Issue
Block a user