-Refactored serializing and deserializing lists and dictionary

This commit is contained in:
James Newton-King
2013-03-29 15:52:57 +13:00
parent 70f5a1dced
commit efd31db09e
6 changed files with 250 additions and 179 deletions
@@ -464,6 +464,8 @@ keyword such as type of business.""
string jsonText = JsonConvert.SerializeObject(r1);
Assert.AreEqual("[0,1,2,3,4]", jsonText);
ReadOnlyCollection<int> r2 = JsonConvert.DeserializeObject<ReadOnlyCollection<int>>(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<InvalidOperationException>(
"Cannot create and populate list type " + classRef + ".",
ExceptionAssert.Throws<JsonSerializationException>(
"Cannot create and populate list type " + classRef + ". Path 'StringDictionaryProperty', line 2, position 32.",
() =>
{
JsonConvert.DeserializeObject<StringDictionaryTestClass>(json);
@@ -5311,6 +5313,7 @@ To fix this error either change the environment to be fully trusted, change the
{
BusRun r = JsonConvert.DeserializeObject<BusRun>("{\"Departures\":[\"\\/Date(1309874148734-0400)\\/\",\"\\/Date(1309874148739-0400)\\/\",null],\"WheelchairAccessible\":true}");
Assert.AreEqual(typeof(List<DateTime?>), 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<T> : IEnumerable<T>
{
private readonly IList<T> _values;
public EnumerableClassFailure()
{
_values = new List<T>();
}
public IEnumerator<T> GetEnumerator()
{
return _values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
[Test]
public void DeserializeIEnumerableFromConstructor_Failure()
{
string json = @"[
""One"",
""II"",
""3""
]";
ExceptionAssert.Throws<JsonSerializationException>(
"Cannot create and populate list type Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+EnumerableClassFailure`1[System.String]. Path '', line 1, position 1.",
() => JsonConvert.DeserializeObject<EnumerableClassFailure<string>>(json));
}
[JsonObject(MemberSerialization.Fields)]
public class MyTuple<T1>
{
@@ -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<object, object> _genericWrapperCreator;
private Func<object> _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<ParameterInfo> parameters = constructor.GetParameters();
if (parameters.Count == 1)
{
if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType))
return constructor;
}
}
return null;
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonArrayContract"/> 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<object>);
}
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<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))
{
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<object>(TemporaryCollectionType);
return (IList)_genericTemporaryCollectionCreator();
}
private void EnsureGenericWrapperCreator()
{
if (_genericWrapperCreator == null)
@@ -67,6 +67,9 @@ namespace Newtonsoft.Json.Serialization
private Func<object> _genericTemporaryDictionaryCreator;
internal bool ShouldCreateWrapper { get; private set; }
internal bool IsReadOnlyOrFixedSize { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonDictionaryContract"/> class.
/// </summary>
@@ -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<object>(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; }
}
}
@@ -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<T> 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<object>) 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<object>)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)
@@ -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)
@@ -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<object>();
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<ParameterInfo> 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<T> 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<object>();
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<ParameterInfo> 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)