From fe001bd5e6128639a963f903e9eede75c4154c45 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 29 Apr 2016 18:30:38 +1200 Subject: [PATCH] -Fixed incorrect check when looking for non-default constructors on collections --- .../JsonSerializerCollectionsTests.cs | 80 +++++++++++++++++++ Src/Newtonsoft.Json/Linq/JTokenWriter.cs | 2 + .../Serialization/JsonDictionaryContract.cs | 5 +- .../Utilities/CollectionUtils.cs | 13 ++- 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerCollectionsTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerCollectionsTests.cs index a7196a4d..b71149ea 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerCollectionsTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerCollectionsTests.cs @@ -1917,6 +1917,86 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(deserializeTest2["testkey"], ""); } #endif + + [Test] + public void DeserializeCollectionWithConstructorArrayArgument() + { + var v = new ReadOnlyCollectionWithArrayArgument(new[] { -0.014147478859765236, -0.011419606805541858, -0.010038461483676238 }); + var json = JsonConvert.SerializeObject(v); + + ExceptionAssert.Throws(() => + { + JsonConvert.DeserializeObject>(json); + }, "Unable to find a constructor to use for type Newtonsoft.Json.Tests.Serialization.ReadOnlyCollectionWithArrayArgument`1[System.Double]. Path '', line 1, position 1."); + } + } + + public class ReadOnlyCollectionWithArrayArgument : IList + { + private readonly IList _values; + + public ReadOnlyCollectionWithArrayArgument(T[] args) + { + _values = args ?? (IList)new List(); + } + + public IEnumerator GetEnumerator() + { + return _values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _values.GetEnumerator(); + } + + public void Add(T item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(T item) + { + throw new NotImplementedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool Remove(T item) + { + throw new NotImplementedException(); + } + + public int Count { get; } + public bool IsReadOnly { get; } + public int IndexOf(T item) + { + throw new NotImplementedException(); + } + + public void Insert(int index, T item) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + public T this[int index] + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } } #if !(NET40 || NET35 || NET20 || PORTABLE40) diff --git a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs index ee571d60..86e5d954 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs @@ -489,6 +489,8 @@ namespace Newtonsoft.Json.Linq internal override void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate, bool writeComments) { JTokenReader tokenReader = reader as JTokenReader; + + // closing the token wrather than reading then writing it doesn't lose some type information, e.g. Guid, byte[], etc if (tokenReader != null && writeChildren && writeDateConstructorAsDate && writeComments) { if (tokenReader.TokenType == JsonToken.None) diff --git a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs index ab3829c5..ab566543 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -172,7 +172,10 @@ namespace Newtonsoft.Json.Serialization if (keyType != null && valueType != null) { - _parameterizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor(CreatedType, typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType)); + _parameterizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor( + CreatedType, + typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType), + typeof(IDictionary<,>).MakeGenericType(keyType, valueType)); #if !(NET35 || NET20) if (!HasParameterizedCreatorInternal && underlyingType.Name == FSharpUtils.FSharpMapTypeName) diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index 1ff8d237..bd504720 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -113,6 +113,13 @@ namespace Newtonsoft.Json.Utilities } public static ConstructorInfo ResolveEnumerableCollectionConstructor(Type collectionType, Type collectionItemType) + { + Type genericConstructorArgument = typeof(IList<>).MakeGenericType(collectionItemType); + + return ResolveEnumerableCollectionConstructor(collectionType, collectionItemType, genericConstructorArgument); + } + + public static ConstructorInfo ResolveEnumerableCollectionConstructor(Type collectionType, Type collectionItemType, Type constructorArgumentType) { Type genericEnumerable = typeof(IEnumerable<>).MakeGenericType(collectionItemType); ConstructorInfo match = null; @@ -123,7 +130,9 @@ namespace Newtonsoft.Json.Utilities if (parameters.Count == 1) { - if (genericEnumerable == parameters[0].ParameterType) + Type parameterType = parameters[0].ParameterType; + + if (genericEnumerable == parameterType) { // exact match match = constructor; @@ -133,7 +142,7 @@ namespace Newtonsoft.Json.Utilities // incase we can't find an exact match, use first inexact if (match == null) { - if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType)) + if (parameterType.IsAssignableFrom(constructorArgumentType)) { match = constructor; }