From efd31db09e594332f12d4190c8937e60c2601028 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 29 Mar 2013 15:52:57 +1300 Subject: [PATCH] -Refactored serializing and deserializing lists and dictionary --- .../Serialization/JsonSerializerTest.cs | 41 +++++- .../Serialization/JsonArrayContract.cs | 101 ++++++++++++- .../Serialization/JsonDictionaryContract.cs | 15 +- .../JsonSerializerInternalReader.cs | 139 +++++++++++------- .../JsonSerializerInternalWriter.cs | 37 ++--- .../Utilities/CollectionUtils.cs | 96 ------------ 6 files changed, 250 insertions(+), 179 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index b604d5f2..3876d65c 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -464,6 +464,8 @@ keyword such as type of business."" string jsonText = JsonConvert.SerializeObject(r1); + Assert.AreEqual("[0,1,2,3,4]", jsonText); + ReadOnlyCollection r2 = JsonConvert.DeserializeObject>(jsonText); CollectionAssert.AreEqual(r1, r2); @@ -4260,8 +4262,8 @@ To fix this error either change the environment to be fully trusted, change the string json = JsonConvert.SerializeObject(s1, Formatting.Indented); - ExceptionAssert.Throws( - "Cannot create and populate list type " + classRef + ".", + ExceptionAssert.Throws( + "Cannot create and populate list type " + classRef + ". Path 'StringDictionaryProperty', line 2, position 32.", () => { JsonConvert.DeserializeObject(json); @@ -5311,6 +5313,7 @@ To fix this error either change the environment to be fully trusted, change the { BusRun r = JsonConvert.DeserializeObject("{\"Departures\":[\"\\/Date(1309874148734-0400)\\/\",\"\\/Date(1309874148739-0400)\\/\",null],\"WheelchairAccessible\":true}"); + Assert.AreEqual(typeof(List), r.Departures.GetType()); Assert.AreEqual(3, r.Departures.Count()); Assert.IsNotNull(r.Departures.ElementAt(0)); Assert.IsNotNull(r.Departures.ElementAt(1)); @@ -6748,6 +6751,40 @@ Parameter name: value", Assert.AreEqual("3", result.ElementAt(2)); } + public class EnumerableClassFailure : IEnumerable + { + private readonly IList _values; + + public EnumerableClassFailure() + { + _values = new List(); + } + + public IEnumerator GetEnumerator() + { + return _values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + [Test] + public void DeserializeIEnumerableFromConstructor_Failure() + { + string json = @"[ + ""One"", + ""II"", + ""3"" +]"; + + ExceptionAssert.Throws( + "Cannot create and populate list type Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+EnumerableClassFailure`1[System.String]. Path '', line 1, position 1.", + () => JsonConvert.DeserializeObject>(json)); + } + [JsonObject(MemberSerialization.Fields)] public class MyTuple { diff --git a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs index 1303caf3..9fd118e2 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs @@ -26,6 +26,8 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; using System.Reflection; using Newtonsoft.Json.Utilities; using System.Collections; @@ -56,8 +58,33 @@ namespace Newtonsoft.Json.Serialization private readonly bool _isCollectionItemTypeNullableType; private readonly Type _genericCollectionDefinitionType; + private Type _genericWrapperType; private MethodCall _genericWrapperCreator; + private Func _genericTemporaryCollectionCreator; + + internal bool ShouldCreateWrapper { get; private set; } + internal bool CanDeserialize { get; private set; } + internal Type TemporaryCollectionType { get; private set; } + internal bool IsReadOnlyOrFixedSize { get; private set; } + + private ConstructorInfo ResolveReadOnlyCollectionConstructor(Type readOnlyCollectionType, Type collectionTypeType) + { + Type genericEnumerable = ReflectionUtils.MakeGenericType(typeof(IEnumerable<>), collectionTypeType); + + foreach (ConstructorInfo constructor in readOnlyCollectionType.GetConstructors()) + { + IList parameters = constructor.GetParameters(); + + if (parameters.Count == 1) + { + if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType)) + return constructor; + } + } + + return null; + } /// /// Initializes a new instance of the class. @@ -67,27 +94,74 @@ namespace Newtonsoft.Json.Serialization : base(underlyingType) { ContractType = JsonContractType.Array; - - if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType)) + + bool canDeserialize; + + Type tempCollectionType; + if (CreatedType.IsArray) + { + CollectionItemType = ReflectionUtils.GetCollectionItemType(UnderlyingType); + IsReadOnlyOrFixedSize = true; + + canDeserialize = true; + TemporaryCollectionType = typeof (List); + } + else if (ReflectionUtils.InheritsGenericDefinition(underlyingType, typeof(ReadOnlyCollection<>), out tempCollectionType)) + { + CollectionItemType = tempCollectionType.GetGenericArguments()[0]; + IsReadOnlyOrFixedSize = true; + + canDeserialize = (ResolveReadOnlyCollectionConstructor(underlyingType, CollectionItemType) != null); + TemporaryCollectionType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType); + } + else if (typeof(IList).IsAssignableFrom(underlyingType)) + { + if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType)) + CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + else + CollectionItemType = ReflectionUtils.GetCollectionItemType(underlyingType); + + canDeserialize = true; + } + else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType)) { CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + canDeserialize = true; + ShouldCreateWrapper = true; } else if (underlyingType.IsGenericType() && underlyingType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { - _genericCollectionDefinitionType = typeof (IEnumerable<>); + _genericCollectionDefinitionType = typeof(IEnumerable<>); CollectionItemType = underlyingType.GetGenericArguments()[0]; + + IsReadOnlyOrFixedSize = true; + canDeserialize = true; + } + else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IEnumerable<>), out tempCollectionType)) + { + CollectionItemType = tempCollectionType.GetGenericArguments()[0]; + + IsReadOnlyOrFixedSize = true; + canDeserialize = (ResolveReadOnlyCollectionConstructor(underlyingType, CollectionItemType) != null); + TemporaryCollectionType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType); } #if !(NET40 || NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE) else if (underlyingType.IsGenericType() && underlyingType.GetGenericTypeDefinition() == typeof(IReadOnlyCollection<>)) { CollectionItemType = underlyingType.GetGenericArguments()[0]; + IsReadOnlyOrFixedSizeCollection = true; + + _readOnlyCollectionConstructor = ResolveReadOnlyCollectionConstructor(underlyingType, CollectionItemType); } #endif else { - CollectionItemType = ReflectionUtils.GetCollectionItemType(UnderlyingType); + // types that implement IEnumerable and nothing else + canDeserialize = false; } + CanDeserialize = canDeserialize; + if (CollectionItemType != null) _isCollectionItemTypeNullableType = ReflectionUtils.IsNullableType(CollectionItemType); @@ -102,6 +176,17 @@ namespace Newtonsoft.Json.Serialization CreatedType = ReflectionUtils.MakeGenericType(typeof(HashSet<>), CollectionItemType); #endif +#if (NET20 || NET35) + // bug in .NET 2.0 & 3.5 that List> 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)) + { + Type tempCollectionItemType = tempCollectionType.GetGenericArguments()[0]; + if (ReflectionUtils.IsNullableType(tempCollectionItemType)) + _createWrapper = true; + } +#endif + IsMultidimensionalArray = (UnderlyingType.IsArray && UnderlyingType.GetArrayRank() > 1); } @@ -135,6 +220,14 @@ namespace Newtonsoft.Json.Serialization } } + internal IList CreateTemporaryCollection() + { + if (_genericTemporaryCollectionCreator == null) + _genericTemporaryCollectionCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(TemporaryCollectionType); + + return (IList)_genericTemporaryCollectionCreator(); + } + private void EnsureGenericWrapperCreator() { if (_genericWrapperCreator == null) diff --git a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs index b0062ae2..7d529aa2 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -67,6 +67,9 @@ namespace Newtonsoft.Json.Serialization private Func _genericTemporaryDictionaryCreator; + internal bool ShouldCreateWrapper { get; private set; } + internal bool IsReadOnlyOrFixedSize { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -78,6 +81,7 @@ namespace Newtonsoft.Json.Serialization Type keyType; Type valueType; + if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IDictionary<,>), out _genericCollectionDefinitionType)) { keyType = _genericCollectionDefinitionType.GetGenericArguments()[0]; @@ -88,6 +92,8 @@ namespace Newtonsoft.Json.Serialization { keyType = _genericCollectionDefinitionType.GetGenericArguments()[0]; valueType = _genericCollectionDefinitionType.GetGenericArguments()[1]; + + IsReadOnlyOrFixedSize = true; } #endif else @@ -95,6 +101,8 @@ namespace Newtonsoft.Json.Serialization ReflectionUtils.GetDictionaryKeyValueTypes(UnderlyingType, out keyType, out valueType); } + ShouldCreateWrapper = !typeof (IDictionary).IsAssignableFrom(underlyingType); + DictionaryKeyType = keyType; DictionaryValueType = valueType; @@ -108,7 +116,6 @@ namespace Newtonsoft.Json.Serialization #if !(NET40 || NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE) else if (IsTypeGenericReadOnlyDictionaryInterface(UnderlyingType)) { - IsReadOnlyDictionary = true; CreatedType = ReflectionUtils.MakeGenericType(typeof(ReadOnlyDictionary<,>), keyType, valueType); } #endif @@ -134,7 +141,7 @@ namespace Newtonsoft.Json.Serialization return (IWrappedDictionary)_genericWrapperCreator(null, dictionary); } - internal object CreateTemporaryDictionary() + internal IDictionary CreateTemporaryDictionary() { if (_genericTemporaryDictionaryCreator == null) { @@ -143,7 +150,7 @@ namespace Newtonsoft.Json.Serialization _genericTemporaryDictionaryCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(temporaryDictionaryType); } - return _genericTemporaryDictionaryCreator(); + return (IDictionary)_genericTemporaryDictionaryCreator(); } private bool IsTypeGenericDictionaryInterface(Type type) @@ -167,7 +174,5 @@ namespace Newtonsoft.Json.Serialization return (genericDefinition == typeof(IReadOnlyDictionary<,>)); } #endif - - internal bool IsReadOnlyDictionary { get; set; } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 66c2ef67..de013ebf 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -81,9 +81,15 @@ namespace Newtonsoft.Json.Serialization if (reader.TokenType == JsonToken.StartArray) { if (contract.ContractType == JsonContractType.Array) - PopulateList(CollectionUtils.CreateCollectionWrapper(target), reader, (JsonArrayContract) contract, null, null); + { + JsonArrayContract arrayContract = (JsonArrayContract) contract; + + PopulateList((arrayContract.ShouldCreateWrapper) ? arrayContract.CreateWrapper(target) : (IList)target, reader, arrayContract, null, null); + } else + { throw JsonSerializationException.Create(reader, "Cannot populate JSON array onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + } } else if (reader.TokenType == JsonToken.StartObject) { @@ -98,11 +104,18 @@ namespace Newtonsoft.Json.Serialization } if (contract.ContractType == JsonContractType.Dictionary) - PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(target), reader, (JsonDictionaryContract) contract, null, id); + { + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)contract; + PopulateDictionary((dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(target) : (IDictionary)target, reader, dictionaryContract, null, id); + } else if (contract.ContractType == JsonContractType.Object) + { PopulateObject(target, reader, (JsonObjectContract) contract, null, id); + } else + { throw JsonSerializationException.Create(reader, "Cannot populate JSON object onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + } } else { @@ -389,21 +402,17 @@ namespace Newtonsoft.Json.Serialization break; case JsonContractType.Dictionary: JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) contract; - bool isTemporaryDictionary = false; object targetDictionary; if (existingValue != null) - targetDictionary = existingValue; + targetDictionary = dictionaryContract.ShouldCreateWrapper ? dictionaryContract.CreateWrapper(existingValue) : existingValue; else - targetDictionary = CreateNewDictionary(reader, dictionaryContract, out isTemporaryDictionary); + targetDictionary = CreateNewDictionary(reader, dictionaryContract); - object dictionary = PopulateDictionary(dictionaryContract.CreateWrapper(targetDictionary), reader, dictionaryContract, member, id); + object dictionary = PopulateDictionary((IDictionary) targetDictionary, reader, dictionaryContract, member, id); - if (isTemporaryDictionary) + if (dictionaryContract.IsReadOnlyOrFixedSize) { - if (dictionaryContract.IsReadOnlyDictionary) - { - dictionary = ReflectionUtils.CreateInstance(contract.CreatedType, dictionary); - } + dictionary = ReflectionUtils.CreateInstance(contract.CreatedType, dictionary); } return dictionary; @@ -575,37 +584,33 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (existingValue == null) { - bool isTemporaryListReference; - IList list = CollectionUtils.CreateList(contract.CreatedType, out isTemporaryListReference); - - if (id != null && isTemporaryListReference) + if (id != null && arrayContract.IsReadOnlyOrFixedSize) throw JsonSerializationException.Create(reader, "Cannot preserve reference to array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); - if (contract.OnSerializingCallbacks.Count > 0 && isTemporaryListReference) + if (contract.OnSerializingCallbacks.Count > 0 && arrayContract.IsReadOnlyOrFixedSize) throw JsonSerializationException.Create(reader, "Cannot call OnSerializing on an array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); - if (contract.OnErrorCallbacks.Count > 0 && isTemporaryListReference) + if (contract.OnErrorCallbacks.Count > 0 && arrayContract.IsReadOnlyOrFixedSize) throw JsonSerializationException.Create(reader, "Cannot call OnError on an array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + IList list = CreateNewList(reader, arrayContract); + if (!arrayContract.IsMultidimensionalArray) - PopulateList(arrayContract.CreateWrapper(list), reader, arrayContract, member, id); + PopulateList(list, reader, arrayContract, member, id); else PopulateMultidimensionalArray(list, reader, arrayContract, member, id); // create readonly and fixed sized collections using the temporary list - if (isTemporaryListReference) + // not needed for IEnumerable properties. temp list is final + if (arrayContract.IsReadOnlyOrFixedSize && list.GetType() != contract.CreatedType) { if (arrayContract.IsMultidimensionalArray) { - list = CollectionUtils.ToMultidimensionalArray(list, ReflectionUtils.GetCollectionItemType(contract.CreatedType), contract.CreatedType.GetArrayRank()); + list = CollectionUtils.ToMultidimensionalArray(list, arrayContract.CollectionItemType, contract.CreatedType.GetArrayRank()); } else if (contract.CreatedType.IsArray) { - list = CollectionUtils.ToArray(((List) list).ToArray(), ReflectionUtils.GetCollectionItemType(contract.CreatedType)); - } - else if (ReflectionUtils.InheritsGenericDefinition(contract.CreatedType, typeof(ReadOnlyCollection<>))) - { - list = (IList) ReflectionUtils.CreateInstance(contract.CreatedType, list); + list = CollectionUtils.ToArray(((List)list).ToArray(), arrayContract.CollectionItemType); } else { @@ -621,7 +626,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } else { - value = PopulateList(arrayContract.CreateWrapper(existingValue), reader, arrayContract, member, id); + value = PopulateList((arrayContract.ShouldCreateWrapper) ? arrayContract.CreateWrapper(existingValue) : (IList) existingValue, reader, arrayContract, member, id); } } else @@ -832,26 +837,47 @@ To fix this error either change the JSON to a {1} or change the deserialized typ return true; } - public object CreateNewDictionary(JsonReader reader, JsonDictionaryContract contract, out bool isTemporaryDictionary) + private IList CreateNewList(JsonReader reader, JsonArrayContract contract) + { + if (!contract.CanDeserialize) + throw JsonSerializationException.Create(reader, "Cannot create and populate list type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.CreatedType)); + + if (contract.IsReadOnlyOrFixedSize && contract.TemporaryCollectionType != null) + { + return contract.CreateTemporaryCollection(); + } + else + { + object list = contract.DefaultCreator(); + + if (contract.ShouldCreateWrapper) + list = contract.CreateWrapper(list); + + return (IList)list; + } + } + + private IDictionary CreateNewDictionary(JsonReader reader, JsonDictionaryContract contract) { object dictionary; - if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) - { - dictionary = contract.DefaultCreator(); - isTemporaryDictionary = false; - } - else if (contract.IsReadOnlyDictionary) + if (contract.IsReadOnlyOrFixedSize) { dictionary = contract.CreateTemporaryDictionary(); - isTemporaryDictionary = true; + } + else if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + { + dictionary = contract.DefaultCreator(); + + if (contract.ShouldCreateWrapper) + dictionary = contract.CreateWrapper(dictionary); } else { throw JsonSerializationException.Create(reader, "Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); } - return dictionary; + return (IDictionary)dictionary; } private void OnDeserializing(JsonReader reader, JsonContract contract, object value) @@ -870,14 +896,15 @@ To fix this error either change the JSON to a {1} or change the deserialized typ contract.InvokeOnDeserialized(value, Serializer.Context); } - private object PopulateDictionary(IWrappedDictionary wrappedDictionary, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, string id) + private object PopulateDictionary(IDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, string id) { - object dictionary = wrappedDictionary.UnderlyingDictionary; + IWrappedDictionary wrappedDictionary = dictionary as IWrappedDictionary; + object underlyingDictionary = wrappedDictionary != null ? wrappedDictionary.UnderlyingDictionary : dictionary; if (id != null) - AddReference(reader, id, dictionary); + AddReference(reader, id, underlyingDictionary); - OnDeserializing(reader, contract, dictionary); + OnDeserializing(reader, contract, underlyingDictionary); int initialDepth = reader.Depth; @@ -900,6 +927,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { try { + // this is for correctly reading ISO and MS formatted dictionary keys if (contract.DictionaryKeyType == typeof(DateTime) && JsonConvert.TryParseDateTime(keyValue.ToString(), DateParseHandling.DateTime, reader.DateTimeZoneHandling, out keyValue)) { } @@ -927,11 +955,11 @@ To fix this error either change the JSON to a {1} or change the deserialized typ else itemValue = CreateValueInternal(reader, contract.DictionaryValueType, contract.ItemContract, null, contract, containerProperty, null); - wrappedDictionary[keyValue] = itemValue; + dictionary[keyValue] = itemValue; } catch (Exception ex) { - if (IsErrorHandled(dictionary, contract, keyValue, reader as IJsonLineInfo, reader.Path, ex)) + if (IsErrorHandled(underlyingDictionary, contract, keyValue, reader as IJsonLineInfo, reader.Path, ex)) HandleError(reader, true, initialDepth); else throw; @@ -948,10 +976,10 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } while (!finished && reader.Read()); if (!finished) - ThrowUnexpectedEndException(reader, contract, dictionary, "Unexpected end when deserializing object."); + ThrowUnexpectedEndException(reader, contract, underlyingDictionary, "Unexpected end when deserializing object."); - OnDeserialized(reader, contract, dictionary); - return dictionary; + OnDeserialized(reader, contract, underlyingDictionary); + return underlyingDictionary; } private object PopulateMultidimensionalArray(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id) @@ -1092,21 +1120,22 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } } - private object PopulateList(IWrappedCollection wrappedList, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id) + private object PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id) { - object list = wrappedList.UnderlyingCollection; + IWrappedCollection wrappedCollection = list as IWrappedCollection; + object underlyingList = wrappedCollection != null ? wrappedCollection.UnderlyingCollection : list; if (id != null) - AddReference(reader, id, list); + AddReference(reader, id, underlyingList); // can't populate an existing array - if (wrappedList.IsFixedSize) + if (list.IsFixedSize) { reader.Skip(); - return list; + return underlyingList; } - OnDeserializing(reader, contract, list); + OnDeserializing(reader, contract, underlyingList); int initialDepth = reader.Depth; @@ -1137,7 +1166,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ else value = CreateValueInternal(reader, contract.CollectionItemType, collectionItemContract, null, contract, containerProperty, null); - wrappedList.Add(value); + list.Add(value); break; } } @@ -1150,7 +1179,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { JsonPosition errorPosition = reader.GetPosition(initialDepth); - if (IsErrorHandled(list, contract, errorPosition.Position, reader as IJsonLineInfo, reader.Path, ex)) + if (IsErrorHandled(underlyingList, contract, errorPosition.Position, reader as IJsonLineInfo, reader.Path, ex)) { HandleError(reader, true, initialDepth); @@ -1173,10 +1202,10 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } while (!finished); if (!finished) - ThrowUnexpectedEndException(reader, contract, list, "Unexpected end when deserializing array."); + ThrowUnexpectedEndException(reader, contract, underlyingList, "Unexpected end when deserializing array."); - OnDeserialized(reader, contract, list); - return list; + OnDeserialized(reader, contract, underlyingList); + return underlyingList; } #if !(SILVERLIGHT || NETFX_CORE || PORTABLE) diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 47c0855f..bb674b2b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -139,7 +139,7 @@ namespace Newtonsoft.Json.Serialization case JsonContractType.Array: JsonArrayContract arrayContract = (JsonArrayContract) valueContract; if (!arrayContract.IsMultidimensionalArray) - SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, containerContract, containerProperty); + SerializeList(writer, (value.ShouldCreateWrapper) ? arrayContract.CreateWrapper(value) : (IList) value, arrayContract, member, containerContract, containerProperty); else SerializeMultidimensionalArray(writer, (Array)value, arrayContract, member, containerContract, containerProperty); break; @@ -520,13 +520,16 @@ namespace Newtonsoft.Json.Serialization } } - private void SerializeList(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + private void SerializeList(JsonWriter writer, IList values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) { - OnSerializing(writer, contract, values.UnderlyingCollection); + IWrappedCollection wrappedCollection = values as IWrappedCollection; + object underlyingList = wrappedCollection != null ? wrappedCollection.UnderlyingCollection : values; - _serializeStack.Add(values.UnderlyingCollection); + OnSerializing(writer, contract, underlyingList); - bool hasWrittenMetadataObject = WriteStartArray(writer, values.UnderlyingCollection, contract, member, collectionContract, containerProperty); + _serializeStack.Add(underlyingList); + + bool hasWrittenMetadataObject = WriteStartArray(writer, underlyingList, contract, member, collectionContract, containerProperty); writer.WriteStartArray(); @@ -554,7 +557,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - if (IsErrorHandled(values.UnderlyingCollection, contract, index, null, writer.ContainerPath, ex)) + if (IsErrorHandled(underlyingList, contract, index, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth); else throw; @@ -572,7 +575,7 @@ namespace Newtonsoft.Json.Serialization _serializeStack.RemoveAt(_serializeStack.Count - 1); - OnSerialized(writer, contract, values.UnderlyingCollection); + OnSerialized(writer, contract, underlyingList); } private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) @@ -817,22 +820,22 @@ To fix this error either change the environment to be fully trusted, change the return false; } - private void SerializeDictionary(JsonWriter writer, IWrappedDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + private void SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) { - OnSerializing(writer, contract, values.UnderlyingDictionary); - _serializeStack.Add(values.UnderlyingDictionary); + IWrappedDictionary wrappedDictionary = values as IWrappedDictionary; + object underlyingDictionary = wrappedDictionary != null ? wrappedDictionary.UnderlyingDictionary : values; - WriteObjectStart(writer, values.UnderlyingDictionary, contract, member, collectionContract, containerProperty); + OnSerializing(writer, contract, underlyingDictionary); + _serializeStack.Add(underlyingDictionary); + + WriteObjectStart(writer, underlyingDictionary, contract, member, collectionContract, containerProperty); if (contract.ItemContract == null) contract.ItemContract = Serializer.ContractResolver.ResolveContract(contract.DictionaryValueType ?? typeof(object)); int initialDepth = writer.Top; - // Mono Unity 3.0 fix - IWrappedDictionary d = values; - - foreach (DictionaryEntry entry in d) + foreach (DictionaryEntry entry in values) { bool escape; string propertyName = GetPropertyName(writer, entry, out escape); @@ -863,7 +866,7 @@ To fix this error either change the environment to be fully trusted, change the } catch (Exception ex) { - if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex)) + if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth); else throw; @@ -874,7 +877,7 @@ To fix this error either change the environment to be fully trusted, change the _serializeStack.RemoveAt(_serializeStack.Count - 1); - OnSerialized(writer, contract, values.UnderlyingDictionary); + OnSerialized(writer, contract, underlyingDictionary); } private string GetPropertyName(JsonWriter writer, DictionaryEntry entry, out bool escape) diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index 41f82baf..8f294191 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -171,102 +171,6 @@ namespace Newtonsoft.Json.Utilities } } - public static IList CreateList(Type listType, out bool isReadOnlyOrFixedSize) - { - ValidationUtils.ArgumentNotNull(listType, "listType"); - - IList list; - Type collectionType; - isReadOnlyOrFixedSize = false; - - if (listType.IsArray) - { - // have to use an arraylist when creating array - // there is no way to know the size until it is finised - list = new List(); - isReadOnlyOrFixedSize = true; - } - else if (ReflectionUtils.InheritsGenericDefinition(listType, typeof(ReadOnlyCollection<>), out collectionType)) - { - Type readOnlyCollectionContentsType = collectionType.GetGenericArguments()[0]; - Type genericEnumerable = ReflectionUtils.MakeGenericType(typeof(IEnumerable<>), readOnlyCollectionContentsType); - bool suitableConstructor = false; - - foreach (ConstructorInfo constructor in listType.GetConstructors()) - { - IList parameters = constructor.GetParameters(); - - if (parameters.Count == 1) - { - if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType)) - { - suitableConstructor = true; - break; - } - } - } - - if (!suitableConstructor) - throw new Exception("Read-only type {0} does not have a public constructor that takes a type that implements {1}.".FormatWith(CultureInfo.InvariantCulture, listType, genericEnumerable)); - - // can't add or modify a readonly list - // use List and convert once populated - list = CreateGenericList(readOnlyCollectionContentsType); - isReadOnlyOrFixedSize = true; - } - else if (typeof(IList).IsAssignableFrom(listType)) - { - if (ReflectionUtils.IsInstantiatableType(listType)) - list = (IList)Activator.CreateInstance(listType); - else if (listType == typeof(IList)) - list = new List(); - else - list = null; - } - else if (ReflectionUtils.ImplementsGenericDefinition(listType, typeof(ICollection<>))) - { - if (ReflectionUtils.IsInstantiatableType(listType)) - list = CreateCollectionWrapper(Activator.CreateInstance(listType)); - else - list = null; - } - else if (ReflectionUtils.ImplementsGenericDefinition(listType, typeof (IEnumerable<>), out collectionType)) - { - bool suitableConstructor = false; - - foreach (ConstructorInfo constructor in listType.GetConstructors()) - { - IList parameters = constructor.GetParameters(); - - if (parameters.Count == 1) - { - if (collectionType.IsAssignableFrom(parameters[0].ParameterType)) - { - suitableConstructor = true; - break; - } - } - } - - if (!suitableConstructor) - throw new Exception("{0} does not have a public constructor that takes a type that implements {1}.".FormatWith(CultureInfo.InvariantCulture, listType, collectionType)); - - Type collectionContentsType = collectionType.GetGenericArguments()[0]; - - list = CreateGenericList(collectionContentsType); - isReadOnlyOrFixedSize = true; - } - else - { - list = null; - } - - if (list == null) - throw new InvalidOperationException("Cannot create and populate list type {0}.".FormatWith(CultureInfo.InvariantCulture, listType)); - - return list; - } - public static Array ToArray(Array initial, Type type) { if (type == null)