-Added support for deserializing IList

-Fixed bugs
This commit is contained in:
James Newton-King
2013-03-30 20:46:11 +13:00
parent bef6c4c11a
commit 0007006850
10 changed files with 118 additions and 129 deletions
@@ -75,4 +75,4 @@ using System.Security;
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
[assembly: AssemblyFileVersion("5.0.1.15929")]
[assembly: AssemblyFileVersion("5.0.1.15930")]
@@ -4186,8 +4186,16 @@ To fix this error either change the environment to be fully trusted, change the
4.3,
null
]", jsonText);
}
double?[] d = (double?[])JsonConvert.DeserializeObject(jsonText, typeof(double?[]));
[Test]
public void DeserializeNullableArray()
{
double?[] d = (double?[])JsonConvert.DeserializeObject(@"[
2.4,
4.3,
null
]", typeof(double?[]));
Assert.AreEqual(3, d.Length);
Assert.AreEqual(2.4, d[0]);
@@ -6747,17 +6755,17 @@ Parameter name: value",
public void DeserializeIEnumerableFromConstructor()
{
string json = @"[
""One"",
""II"",
""3""
1,
2,
null
]";
var result = JsonConvert.DeserializeObject<EnumerableClass<string>>(json);
var result = JsonConvert.DeserializeObject<EnumerableClass<int?>>(json);
Assert.AreEqual(3, result.Count());
Assert.AreEqual("One", result.ElementAt(0));
Assert.AreEqual("II", result.ElementAt(1));
Assert.AreEqual("3", result.ElementAt(2));
Assert.AreEqual(1, result.ElementAt(0));
Assert.AreEqual(2, result.ElementAt(1));
Assert.AreEqual(null, result.ElementAt(2));
}
public class EnumerableClassFailure<T> : IEnumerable<T>
@@ -6859,6 +6867,20 @@ Parameter name: value",
Assert.AreEqual(BigInteger.Parse("9000000000000000000000000000000000000000000000000"), nineQuindecillion);
}
[Test]
public void DeserializeReadOnlyListWithNullableType()
{
string json = @"[
1,
null
]";
var l = JsonConvert.DeserializeObject<IReadOnlyList<int?>>(json);
Assert.AreEqual(1, l[0]);
Assert.AreEqual(null, l[1]);
}
#endif
[Test]
@@ -7787,7 +7809,18 @@ Parameter name: value",
}
#endif
[Test]
public void DeserializeNonGenericList()
{
IList l = JsonConvert.DeserializeObject<IList>("['string!']");
Assert.AreEqual(typeof(List<object>), l.GetType());
Assert.AreEqual(1, l.Count);
Assert.AreEqual("string!", l[0]);
}
#if !(NET20 || NET35 || SILVERLIGHT || PORTABLE)
[Test]
public void SerializeBigInteger()
{
BigInteger i = BigInteger.Parse("123456789999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999990");
@@ -652,20 +652,20 @@ namespace Newtonsoft.Json.Tests.Serialization
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Error += (obj, args) =>
{
args.ErrorContext.Handled = true;
};
var data = new List<ErrorPerson2>()
{
new ErrorPerson2{FirstName = "Scott", LastName = "Hanselman"},
new ErrorPerson2{FirstName = "Scott", LastName = "Hunter"},
new ErrorPerson2{FirstName = "Scott", LastName = "Guthrie"},
args.ErrorContext.Handled = true;
};
var dictionary = data.GroupBy(person => person.FirstName).ToDictionary(group => @group.Key, group => @group.Cast<IErrorPerson2>());
var output = JsonConvert.SerializeObject(dictionary, Formatting.None, settings);
Assert.AreEqual(@"{""Scott"":null}", output);
var data = new List<ErrorPerson2>()
{
new ErrorPerson2 {FirstName = "Scott", LastName = "Hanselman"},
new ErrorPerson2 {FirstName = "Scott", LastName = "Hunter"},
new ErrorPerson2 {FirstName = "Scott", LastName = "Guthrie"},
};
Dictionary<string, IEnumerable<IErrorPerson2>> dictionary = data.GroupBy(person => person.FirstName).ToDictionary(group => @group.Key, group => @group.Cast<IErrorPerson2>());
string output = JsonConvert.SerializeObject(dictionary, Formatting.None, settings);
Assert.AreEqual(@"{""Scott"":[]}", output);
}
[Test]
@@ -685,10 +685,10 @@ namespace Newtonsoft.Json.Tests.Serialization
new ErrorPerson2 {FirstName = "James", LastName = "Newton-King"},
};
var dictionary = data.GroupBy(person => person.FirstName).ToDictionary(group => @group.Key, group => @group.Cast<IErrorPerson2>());
var output = JsonConvert.SerializeObject(dictionary, Formatting.None, settings);
Dictionary<string, IEnumerable<IErrorPerson2>> dictionary = data.GroupBy(person => person.FirstName).ToDictionary(group => @group.Key, group => @group.Cast<IErrorPerson2>());
string output = JsonConvert.SerializeObject(dictionary, Formatting.None, settings);
Assert.AreEqual(@"{""Scott"":null,""James"":null}", output);
Assert.AreEqual(@"{""Scott"":[],""James"":[]}", output);
}
}
@@ -867,7 +867,7 @@ namespace Newtonsoft.Json.Tests.Serialization
[Test]
public void SerializingIEnumerableOfTShouldRetainGenericTypeInfo()
{
string productClassRef = ReflectionUtils.GetTypeName(typeof (Product[]), FormatterAssemblyStyle.Simple);
string productClassRef = ReflectionUtils.GetTypeName(typeof(CustomEnumerable<Product>), FormatterAssemblyStyle.Simple);
CustomEnumerable<Product> products = new CustomEnumerable<Product>();
@@ -90,6 +90,6 @@ using System.Security;
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
[assembly: AssemblyFileVersion("5.0.1.15929")]
[assembly: AssemblyFileVersion("5.0.1.15930")]
[assembly: CLSCompliant(true)]
@@ -84,6 +84,7 @@ namespace Newtonsoft.Json.Serialization
{
CollectionItemType = ReflectionUtils.GetCollectionItemType(UnderlyingType);
IsReadOnlyOrFixedSize = true;
_genericCollectionDefinitionType = ReflectionUtils.MakeGenericType(typeof (List<>), CollectionItemType);
canDeserialize = true;
IsMultidimensionalArray = (UnderlyingType.IsArray && UnderlyingType.GetArrayRank() > 1);
@@ -104,6 +105,9 @@ namespace Newtonsoft.Json.Serialization
else
CollectionItemType = ReflectionUtils.GetCollectionItemType(underlyingType);
if (underlyingType == typeof (IList))
CreatedType = typeof (List<object>);
if (CollectionItemType != null)
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(underlyingType, CollectionItemType);
canDeserialize = true;
@@ -111,6 +115,16 @@ namespace Newtonsoft.Json.Serialization
else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType))
{
CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0];
if (ReflectionUtils.IsGenericDefinition(underlyingType, typeof(ICollection<>))
|| ReflectionUtils.IsGenericDefinition(underlyingType, typeof(IList<>)))
CreatedType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
#if !(NET20 || NET35)
if (ReflectionUtils.IsGenericDefinition(underlyingType, typeof(ISet<>)))
CreatedType = ReflectionUtils.MakeGenericType(typeof(HashSet<>), CollectionItemType);
#endif
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(underlyingType, CollectionItemType);
canDeserialize = true;
ShouldCreateWrapper = true;
@@ -119,18 +133,24 @@ namespace Newtonsoft.Json.Serialization
else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof (IReadOnlyCollection<>), out tempCollectionType))
{
CollectionItemType = underlyingType.GetGenericArguments()[0];
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(underlyingType, CollectionItemType);
IsReadOnlyOrFixedSize = true;
ShouldCreateWrapper = !typeof(IList).IsAssignableFrom(underlyingType);
canDeserialize = (ResolveReadOnlyCollectionConstructor(underlyingType, CollectionItemType) != null
|| underlyingType.GetGenericTypeDefinition() == typeof(IReadOnlyCollection<>)
|| underlyingType.GetGenericTypeDefinition() == typeof(IReadOnlyList<>));
if (ReflectionUtils.IsGenericDefinition(underlyingType, typeof (IReadOnlyCollection<>))
|| ReflectionUtils.IsGenericDefinition(underlyingType, typeof (IReadOnlyList<>)))
CreatedType = ReflectionUtils.MakeGenericType(typeof(ReadOnlyCollection<>), CollectionItemType);
_genericCollectionDefinitionType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(CreatedType, CollectionItemType);
IsReadOnlyOrFixedSize = true;
canDeserialize = (ParametrizedConstructor != null);
}
#endif
else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof (IEnumerable<>), out tempCollectionType))
{
CollectionItemType = tempCollectionType.GetGenericArguments()[0];
if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IEnumerable<>)))
CreatedType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(underlyingType, CollectionItemType);
if (underlyingType.GetGenericTypeDefinition() == typeof (IEnumerable<>))
@@ -143,6 +163,8 @@ namespace Newtonsoft.Json.Serialization
}
else
{
_genericCollectionDefinitionType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
IsReadOnlyOrFixedSize = true;
ShouldCreateWrapper = true;
canDeserialize = (ParametrizedConstructor != null);
@@ -160,35 +182,20 @@ namespace Newtonsoft.Json.Serialization
if (CollectionItemType != null)
_isCollectionItemTypeNullableType = ReflectionUtils.IsNullableType(CollectionItemType);
if (IsTypeGenericCollectionInterface(UnderlyingType))
CreatedType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
#if !(NET40 || NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE)
else if (IsTypeGenericReadOnlyCollectionInterface(UnderlyingType))
CreatedType = ReflectionUtils.MakeGenericType(typeof(ReadOnlyCollection<>), CollectionItemType);
#endif
#if !(NET20 || NET35)
else if (IsTypeGenericSetInterface(UnderlyingType))
CreatedType = ReflectionUtils.MakeGenericType(typeof(HashSet<>), CollectionItemType);
#endif
#if (NET20 || NET35)
// bug in .NET 2.0 & 3.5 that List<Nullable<T>> throws an error when adding null via IList.Add(object)
// wrapper will handle calling Add(T) instead
if (ReflectionUtils.InheritsGenericDefinition(CreatedType, typeof(List<>), out tempCollectionType))
if (_isCollectionItemTypeNullableType
&& (ReflectionUtils.InheritsGenericDefinition(CreatedType, typeof(List<>), out tempCollectionType)
|| (CreatedType.IsArray && !IsMultidimensionalArray)))
{
Type tempCollectionItemType = tempCollectionType.GetGenericArguments()[0];
if (ReflectionUtils.IsNullableType(tempCollectionItemType))
ShouldCreateWrapper = true;
ShouldCreateWrapper = true;
}
#endif
}
internal IWrappedCollection CreateWrapper(object list)
{
if ((list is IList && (CollectionItemType == null || !_isCollectionItemTypeNullableType))
|| UnderlyingType.IsArray)
return new CollectionWrapper<object>((IList)list);
if (_genericCollectionDefinitionType != null)
{
EnsureGenericWrapperCreator();
@@ -244,42 +251,5 @@ namespace Newtonsoft.Json.Serialization
_genericWrapperCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(genericWrapperConstructor);
}
}
private bool IsTypeGenericCollectionInterface(Type type)
{
if (!type.IsGenericType())
return false;
Type genericDefinition = type.GetGenericTypeDefinition();
return (genericDefinition == typeof(IList<>)
|| genericDefinition == typeof(ICollection<>)
|| genericDefinition == typeof(IEnumerable<>));
}
#if !(NET40 || NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE)
private bool IsTypeGenericReadOnlyCollectionInterface(Type type)
{
if (!type.IsGenericType())
return false;
Type genericDefinition = type.GetGenericTypeDefinition();
return (genericDefinition == typeof (IReadOnlyCollection<>)
|| genericDefinition == typeof (IReadOnlyList<>));
}
#endif
#if !(NET20 || NET35)
private bool IsTypeGenericSetInterface(Type type)
{
if (!type.IsGenericType())
return false;
Type genericDefinition = type.GetGenericTypeDefinition();
return (genericDefinition == typeof(ISet<>));
}
#endif
}
}
@@ -87,6 +87,9 @@ namespace Newtonsoft.Json.Serialization
{
keyType = _genericCollectionDefinitionType.GetGenericArguments()[0];
valueType = _genericCollectionDefinitionType.GetGenericArguments()[1];
if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IDictionary<,>)))
CreatedType = ReflectionUtils.MakeGenericType(typeof(Dictionary<,>), keyType, valueType);
}
#if !(NET40 || NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE)
else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IReadOnlyDictionary<,>), out _genericCollectionDefinitionType))
@@ -94,16 +97,22 @@ namespace Newtonsoft.Json.Serialization
keyType = _genericCollectionDefinitionType.GetGenericArguments()[0];
valueType = _genericCollectionDefinitionType.GetGenericArguments()[1];
if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IReadOnlyDictionary<,>)))
CreatedType = ReflectionUtils.MakeGenericType(typeof(ReadOnlyDictionary<,>), keyType, valueType);
IsReadOnlyOrFixedSize = true;
}
#endif
else
{
ReflectionUtils.GetDictionaryKeyValueTypes(UnderlyingType, out keyType, out valueType);
if (UnderlyingType == typeof(IDictionary))
CreatedType = typeof(Dictionary<object, object>);
}
if (keyType != null && valueType != null)
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(underlyingType, ReflectionUtils.MakeGenericType(typeof(KeyValuePair<,>), keyType, valueType));
ParametrizedConstructor = CollectionUtils.ResolveEnumableCollectionConstructor(CreatedType, ReflectionUtils.MakeGenericType(typeof(KeyValuePair<,>), keyType, valueType));
ShouldCreateWrapper = !typeof (IDictionary).IsAssignableFrom(underlyingType);
@@ -112,21 +121,6 @@ namespace Newtonsoft.Json.Serialization
if (DictionaryValueType != null)
_isDictionaryValueTypeNullableType = ReflectionUtils.IsNullableType(DictionaryValueType);
if (IsTypeGenericDictionaryInterface(UnderlyingType))
{
CreatedType = ReflectionUtils.MakeGenericType(typeof(Dictionary<,>), keyType, valueType);
}
#if !(NET40 || NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE)
else if (IsTypeGenericReadOnlyDictionaryInterface(UnderlyingType))
{
CreatedType = ReflectionUtils.MakeGenericType(typeof(ReadOnlyDictionary<,>), keyType, valueType);
}
#endif
else if (UnderlyingType == typeof(IDictionary))
{
CreatedType = typeof (Dictionary<object, object>);
}
}
internal IWrappedDictionary CreateWrapper(object dictionary)
@@ -156,27 +150,5 @@ namespace Newtonsoft.Json.Serialization
return (IDictionary)_genericTemporaryDictionaryCreator();
}
private bool IsTypeGenericDictionaryInterface(Type type)
{
if (!type.IsGenericType())
return false;
Type genericDefinition = type.GetGenericTypeDefinition();
return (genericDefinition == typeof(IDictionary<,>));
}
#if !(NET40 || NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE)
private bool IsTypeGenericReadOnlyDictionaryInterface(Type type)
{
if (!type.IsGenericType())
return false;
Type genericDefinition = type.GetGenericTypeDefinition();
return (genericDefinition == typeof(IReadOnlyDictionary<,>));
}
#endif
}
}
@@ -875,7 +875,12 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (contract.IsReadOnlyOrFixedSize)
{
createdFromNonDefaultConstructor = true;
return contract.CreateTemporaryCollection();
IList list = contract.CreateTemporaryCollection();
if (contract.ShouldCreateWrapper)
list = contract.CreateWrapper(list);
return list;
}
else if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor))
{
@@ -139,7 +139,7 @@ namespace Newtonsoft.Json.Serialization
case JsonContractType.Array:
JsonArrayContract arrayContract = (JsonArrayContract) valueContract;
if (!arrayContract.IsMultidimensionalArray)
SerializeList(writer, (value is IList) ? (IList)value : arrayContract.CreateWrapper(value), arrayContract, member, containerContract, containerProperty);
SerializeList(writer, (IEnumerable)value, arrayContract, member, containerContract, containerProperty);
else
SerializeMultidimensionalArray(writer, (Array)value, arrayContract, member, containerContract, containerProperty);
break;
@@ -151,7 +151,7 @@ namespace Newtonsoft.Json.Serialization
break;
case JsonContractType.Dictionary:
JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) valueContract;
SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member, containerContract, containerProperty);
SerializeDictionary(writer, (value is IDictionary) ? (IDictionary) value : dictionaryContract.CreateWrapper(value), dictionaryContract, member, containerContract, containerProperty);
break;
#if !(NET35 || NET20)
case JsonContractType.Dynamic:
@@ -520,7 +520,7 @@ namespace Newtonsoft.Json.Serialization
}
}
private void SerializeList(JsonWriter writer, IList values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
private void SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
{
IWrappedCollection wrappedCollection = values as IWrappedCollection;
object underlyingList = wrappedCollection != null ? wrappedCollection.UnderlyingCollection : values;
@@ -305,6 +305,15 @@ namespace Newtonsoft.Json.Utilities
: t;
}
public static bool IsGenericDefinition(Type type, Type genericInterfaceDefinition)
{
if (!type.IsGenericType())
return false;
Type t = type.GetGenericTypeDefinition();
return (t == genericInterfaceDefinition);
}
public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition)
{
Type implementingType;