-Properties on dynamic objects with JsonProperty/DataMember will now be included in serialized JSON

-Serialize and deserialize multidimensional arrays
This commit is contained in:
James Newton-King
2012-07-31 20:53:26 +12:00
parent 0f76d0285d
commit 32dc957880
12 changed files with 602 additions and 59 deletions
@@ -72,5 +72,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
#if !PocketPC
[assembly: AssemblyFileVersion("4.5.7.15127")]
[assembly: AssemblyFileVersion("4.5.8.15131")]
#endif
@@ -192,6 +192,7 @@ namespace Newtonsoft.Json.Tests.Serialization
});
Assert.AreEqual(@"{
""explicit"": false,
""text"": ""Text!"",
""integer"": 2147483647,
""int"": 0,
@@ -30,6 +30,7 @@ using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters;
using System.Text;
@@ -60,6 +61,8 @@ namespace Newtonsoft.Json.Tests.Serialization
private readonly Dictionary<string, object> _members;
public int Int;
[JsonProperty]
public bool Explicit;
public DynamicChildObject ChildObject { get; set; }
internal Dictionary<string, object> Members
@@ -122,6 +125,7 @@ namespace Newtonsoft.Json.Tests.Serialization
public void SerializeDynamicObject()
{
TestDynamicObject dynamicObject = new TestDynamicObject();
dynamicObject.Explicit = true;
dynamic d = dynamicObject;
d.Int = 1;
@@ -144,6 +148,7 @@ namespace Newtonsoft.Json.Tests.Serialization
string json = JsonConvert.SerializeObject(dynamicObject, Formatting.Indented);
Assert.AreEqual(@"{
""Explicit"": true,
""Decimal"": 99.9,
""Int"": 1,
""ChildObject"": {
@@ -153,6 +158,8 @@ namespace Newtonsoft.Json.Tests.Serialization
}", json);
TestDynamicObject newDynamicObject = JsonConvert.DeserializeObject<TestDynamicObject>(json);
Assert.AreEqual(true, newDynamicObject.Explicit);
d = newDynamicObject;
Assert.AreEqual(99.9, d.Decimal);
@@ -6629,6 +6629,186 @@ Parameter name: value",
}
}
#endif
[Test]
public void SerializeArray2D()
{
Array2D aa = new Array2D();
aa.Before = "Before!";
aa.After = "After!";
aa.Coordinates = new[,] { { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 } };
string json = JsonConvert.SerializeObject(aa);
Assert.AreEqual(@"{""Before"":""Before!"",""Coordinates"":[[1,1],[1,2],[2,1],[2,2]],""After"":""After!""}", json);
}
[Test]
public void SerializeArray3D()
{
Array3D aa = new Array3D();
aa.Before = "Before!";
aa.After = "After!";
aa.Coordinates = new[, ,] { { { 1, 1, 1 }, { 1, 1, 2 } }, { { 1, 2, 1 }, { 1, 2, 2 } }, { { 2, 1, 1 }, { 2, 1, 2 } }, { { 2, 2, 1 }, { 2, 2, 2 } } };
string json = JsonConvert.SerializeObject(aa);
Assert.AreEqual(@"{""Before"":""Before!"",""Coordinates"":[[[1,1,1],[1,1,2]],[[1,2,1],[1,2,2]],[[2,1,1],[2,1,2]],[[2,2,1],[2,2,2]]],""After"":""After!""}", json);
}
[Test]
public void DeserializeArray2D()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[1,1],[1,2],[2,1],[2,2]],""After"":""After!""}";
Array2D aa = JsonConvert.DeserializeObject<Array2D>(json);
Assert.AreEqual("Before!", aa.Before);
Assert.AreEqual("After!", aa.After);
Assert.AreEqual(4, aa.Coordinates.GetLength(0));
Assert.AreEqual(2, aa.Coordinates.GetLength(1));
Assert.AreEqual(1, aa.Coordinates[0, 0]);
Assert.AreEqual(2, aa.Coordinates[1, 1]);
string after = JsonConvert.SerializeObject(aa);
Assert.AreEqual(json, after);
}
[Test]
public void DeserializeArray2D_WithTooManyItems()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[1,1],[1,2,3],[2,1],[2,2]],""After"":""After!""}";
ExceptionAssert.Throws<Exception>(
"Cannot deserialize non-cubical array as multidimensional array.",
() => JsonConvert.DeserializeObject<Array2D>(json));
}
[Test]
public void DeserializeArray2D_WithTooFewItems()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[1,1],[1],[2,1],[2,2]],""After"":""After!""}";
ExceptionAssert.Throws<Exception>(
"Cannot deserialize non-cubical array as multidimensional array.",
() => JsonConvert.DeserializeObject<Array2D>(json));
}
[Test]
public void DeserializeArray3D()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[[1,1,1],[1,1,2]],[[1,2,1],[1,2,2]],[[2,1,1],[2,1,2]],[[2,2,1],[2,2,2]]],""After"":""After!""}";
Array3D aa = JsonConvert.DeserializeObject<Array3D>(json);
Assert.AreEqual("Before!", aa.Before);
Assert.AreEqual("After!", aa.After);
Assert.AreEqual(4, aa.Coordinates.GetLength(0));
Assert.AreEqual(2, aa.Coordinates.GetLength(1));
Assert.AreEqual(3, aa.Coordinates.GetLength(2));
Assert.AreEqual(1, aa.Coordinates[0, 0, 0]);
Assert.AreEqual(2, aa.Coordinates[1, 1, 1]);
string after = JsonConvert.SerializeObject(aa);
Assert.AreEqual(json, after);
}
[Test]
public void DeserializeArray3D_WithTooManyItems()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[[1,1,1],[1,1,2,1]],[[1,2,1],[1,2,2]],[[2,1,1],[2,1,2]],[[2,2,1],[2,2,2]]],""After"":""After!""}";
ExceptionAssert.Throws<Exception>(
"Cannot deserialize non-cubical array as multidimensional array.",
() => JsonConvert.DeserializeObject<Array3D>(json));
}
[Test]
public void DeserializeArray3D_WithBadItems()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[[1,1,1],[1,1,2]],[[1,2,1],[1,2,2]],[[2,1,1],[2,1,2]],[[2,2,1],{}]],""After"":""After!""}";
ExceptionAssert.Throws<JsonSerializationException>(
"Unexpected token when deserializing multidimensional array: StartObject. Path 'Coordinates[3][1]', line 1, position 99.",
() => JsonConvert.DeserializeObject<Array3D>(json));
}
[Test]
public void DeserializeArray3D_WithTooFewItems()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[[1,1,1],[1,1]],[[1,2,1],[1,2,2]],[[2,1,1],[2,1,2]],[[2,2,1],[2,2,2]]],""After"":""After!""}";
ExceptionAssert.Throws<Exception>(
"Cannot deserialize non-cubical array as multidimensional array.",
() => JsonConvert.DeserializeObject<Array3D>(json));
}
[Test]
public void SerializeEmpty3DArray()
{
Array3D aa = new Array3D();
aa.Before = "Before!";
aa.After = "After!";
aa.Coordinates = new int[0, 0, 0];
string json = JsonConvert.SerializeObject(aa);
Assert.AreEqual(@"{""Before"":""Before!"",""Coordinates"":[],""After"":""After!""}", json);
}
[Test]
public void DeserializeEmpty3DArray()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[],""After"":""After!""}";
Array3D aa = JsonConvert.DeserializeObject<Array3D>(json);
Assert.AreEqual("Before!", aa.Before);
Assert.AreEqual("After!", aa.After);
Assert.AreEqual(0, aa.Coordinates.GetLength(0));
Assert.AreEqual(0, aa.Coordinates.GetLength(1));
Assert.AreEqual(0, aa.Coordinates.GetLength(2));
string after = JsonConvert.SerializeObject(aa);
Assert.AreEqual(json, after);
}
[Test]
public void DeserializeNull3DArray()
{
string json = @"{""Before"":""Before!"",""Coordinates"":null,""After"":""After!""}";
Array3D aa = JsonConvert.DeserializeObject<Array3D>(json);
Assert.AreEqual("Before!", aa.Before);
Assert.AreEqual("After!", aa.After);
Assert.AreEqual(null, aa.Coordinates);
string after = JsonConvert.SerializeObject(aa);
Assert.AreEqual(json, after);
}
[Test]
public void DeserializeSemiEmpty3DArray()
{
string json = @"{""Before"":""Before!"",""Coordinates"":[[]],""After"":""After!""}";
Array3D aa = JsonConvert.DeserializeObject<Array3D>(json);
Assert.AreEqual("Before!", aa.Before);
Assert.AreEqual("After!", aa.After);
Assert.AreEqual(1, aa.Coordinates.GetLength(0));
Assert.AreEqual(0, aa.Coordinates.GetLength(1));
Assert.AreEqual(0, aa.Coordinates.GetLength(2));
string after = JsonConvert.SerializeObject(aa);
Assert.AreEqual(json, after);
}
}
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
@@ -6939,4 +7119,18 @@ Parameter name: value",
public string IgnoreDataMemberAndDataMemberAttribute { get; set; }
}
#endif
public class Array2D
{
public string Before { get; set; }
public int[,] Coordinates { get; set; }
public string After { get; set; }
}
public class Array3D
{
public string Before { get; set; }
public int[,,] Coordinates { get; set; }
public string After { get; set; }
}
}
@@ -91,7 +91,7 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
#if !PocketPC
[assembly: AssemblyFileVersion("4.5.7.15127")]
[assembly: AssemblyFileVersion("4.5.8.15131")]
#endif
[assembly: CLSCompliant(true)]
@@ -457,8 +457,7 @@ namespace Newtonsoft.Json.Serialization
property.PropertyType = parameterInfo.ParameterType;
bool allowNonPublicAccess;
bool hasExplicitAttribute;
SetPropertySettingsFromAttributes(property, parameterInfo.GetCustomAttributeProvider(), parameterInfo.Name, parameterInfo.Member.DeclaringType, MemberSerialization.OptOut, out allowNonPublicAccess, out hasExplicitAttribute);
SetPropertySettingsFromAttributes(property, parameterInfo.GetCustomAttributeProvider(), parameterInfo.Name, parameterInfo.Member.DeclaringType, MemberSerialization.OptOut, out allowNonPublicAccess);
property.Readable = false;
property.Writable = true;
@@ -905,13 +904,12 @@ namespace Newtonsoft.Json.Serialization
property.ValueProvider = CreateMemberValueProvider(member);
bool allowNonPublicAccess;
bool hasExplicitAttribute;
SetPropertySettingsFromAttributes(property, member.GetCustomAttributeProvider(), member.Name, member.DeclaringType, memberSerialization, out allowNonPublicAccess, out hasExplicitAttribute);
SetPropertySettingsFromAttributes(property, member.GetCustomAttributeProvider(), member.Name, member.DeclaringType, memberSerialization, out allowNonPublicAccess);
if (memberSerialization != MemberSerialization.Fields)
{
property.Readable = ReflectionUtils.CanReadMemberValue(member, allowNonPublicAccess);
property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess, hasExplicitAttribute);
property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess, property.HasMemberAttribute);
}
else
{
@@ -926,10 +924,8 @@ namespace Newtonsoft.Json.Serialization
return property;
}
private void SetPropertySettingsFromAttributes(JsonProperty property, ICustomAttributeProvider attributeProvider, string name, Type declaringType, MemberSerialization memberSerialization, out bool allowNonPublicAccess, out bool hasExplicitAttribute)
private void SetPropertySettingsFromAttributes(JsonProperty property, ICustomAttributeProvider attributeProvider, string name, Type declaringType, MemberSerialization memberSerialization, out bool allowNonPublicAccess)
{
hasExplicitAttribute = false;
#if !PocketPC && !NET20
DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(declaringType);
@@ -949,7 +945,7 @@ namespace Newtonsoft.Json.Serialization
JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute<JsonPropertyAttribute>(attributeProvider);
if (propertyAttribute != null)
hasExplicitAttribute = true;
property.HasMemberAttribute = true;
string mappedName;
if (propertyAttribute != null && propertyAttribute.PropertyName != null)
@@ -1039,7 +1035,7 @@ namespace Newtonsoft.Json.Serialization
if (dataMemberAttribute != null)
{
allowNonPublicAccess = true;
hasExplicitAttribute = true;
property.HasMemberAttribute = true;
}
#endif
}
@@ -47,6 +47,8 @@ namespace Newtonsoft.Json.Serialization
/// <value>The <see cref="Type"/> of the collection items.</value>
public Type CollectionItemType { get; private set; }
public bool IsMultidimensionalArray { get; private set; }
private readonly bool _isCollectionItemTypeNullableType;
private readonly Type _genericCollectionDefinitionType;
private Type _genericWrapperType;
@@ -82,6 +84,8 @@ namespace Newtonsoft.Json.Serialization
{
CreatedType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
}
IsMultidimensionalArray = (UnderlyingType.IsArray && UnderlyingType.GetArrayRank() > 1);
}
internal IWrappedCollection CreateWrapper(object list)
@@ -35,7 +35,7 @@ namespace Newtonsoft.Json.Serialization
/// <summary>
/// Contract details for a <see cref="Type"/> used by the <see cref="JsonSerializer"/>.
/// </summary>
public class JsonDynamicContract : JsonContract
public class JsonDynamicContract : JsonContainerContract
{
/// <summary>
/// Gets the object's properties.
@@ -107,6 +107,12 @@ namespace Newtonsoft.Json.Serialization
/// <value><c>true</c> if writable; otherwise, <c>false</c>.</value>
public bool Writable { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="JsonProperty"/> has a member attribute.
/// </summary>
/// <value><c>true</c> if has a member attribute; otherwise, <c>false</c>.</value>
public bool HasMemberAttribute { get; set; }
/// <summary>
/// Gets the default value.
/// </summary>
@@ -28,6 +28,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
#if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE)
using System.ComponentModel;
using System.Dynamic;
#endif
using System.Globalization;
@@ -554,15 +555,26 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (contract.OnError != null && isTemporaryListReference)
throw JsonSerializationException.Create(reader, "Cannot call OnError on an array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
PopulateList(arrayContract.CreateWrapper(list), reader, arrayContract, member, id);
if (!arrayContract.IsMultidimensionalArray)
PopulateList(arrayContract.CreateWrapper(list), reader, arrayContract, member, id);
else
PopulateMultidimensionalArray(list, reader, arrayContract, member, id);
// create readonly and fixed sized collections using the temporary list
if (isTemporaryListReference)
{
if (contract.CreatedType.IsArray)
list = CollectionUtils.ToArray(((List<object>)list).ToArray(), ReflectionUtils.GetCollectionItemType(contract.CreatedType));
if (arrayContract.IsMultidimensionalArray)
{
list = CollectionUtils.ToMultidimensionalArray(list, ReflectionUtils.GetCollectionItemType(contract.CreatedType), 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 = (IList) ReflectionUtils.CreateInstance(contract.CreatedType, list);
}
}
else if (list is IWrappedCollection)
{
@@ -851,6 +863,125 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object.");
}
private object PopulateMultidimensionalArray(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id)
{
int rank = contract.UnderlyingType.GetArrayRank();
if (id != null)
Serializer.ReferenceResolver.AddReference(this, id, list);
contract.InvokeOnDeserializing(list, Serializer.Context);
int initialDepth = reader.Depth;
JsonContract collectionItemContract = GetContractSafe(contract.CollectionItemType);
JsonConverter collectionItemConverter = GetConverter(collectionItemContract, null, contract, containerProperty);
int? previousErrorIndex = null;
Stack<IList> listStack = new Stack<IList>();
listStack.Push(list);
IList currentList = list;
while (true)
{
if (listStack.Count == rank)
{
try
{
if (ReadForType(reader, collectionItemContract, collectionItemConverter != null))
{
switch (reader.TokenType)
{
case JsonToken.EndArray:
listStack.Pop();
currentList = listStack.Peek();
break;
case JsonToken.Comment:
break;
default:
object value;
if (collectionItemConverter != null && collectionItemConverter.CanRead)
value = collectionItemConverter.ReadJson(reader, contract.CollectionItemType, null, GetInternalSerializer());
else
value = CreateValueInternal(reader, contract.CollectionItemType, collectionItemContract, null, contract, containerProperty, null);
currentList.Add(value);
break;
}
}
else
{
break;
}
}
catch (Exception ex)
{
JsonPosition errorPosition = reader.GetPosition(initialDepth);
if (IsErrorHandled(list, contract, errorPosition.Position, reader.Path, ex))
{
HandleError(reader, true, initialDepth);
if (previousErrorIndex != null && previousErrorIndex == errorPosition.Position)
{
// reader index has not moved since previous error handling
// break out of reading array to prevent infinite loop
throw JsonSerializationException.Create(reader, "Infinite loop detected from error handling.", ex);
}
else
{
previousErrorIndex = errorPosition.Position;
}
}
else
{
throw;
}
}
}
else
{
if (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.StartArray:
IList newList = new List<object>();
currentList.Add(newList);
listStack.Push(newList);
currentList = newList;
break;
case JsonToken.EndArray:
listStack.Pop();
if (listStack.Count > 0)
{
currentList = listStack.Peek();
}
else
{
contract.InvokeOnDeserialized(list, Serializer.Context);
return list;
}
break;
case JsonToken.Comment:
break;
default:
throw JsonSerializationException.Create(reader, "Unexpected token when deserializing multidimensional array: " + reader.TokenType);
}
}
else
{
break;
}
}
}
throw JsonSerializationException.Create(reader, "Unexpected end when deserializing array.");
}
private object PopulateList(IWrappedCollection wrappedList, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id)
{
object list = wrappedList.UnderlyingCollection;
@@ -124,7 +124,10 @@ namespace Newtonsoft.Json.Serialization
break;
case JsonContractType.Array:
JsonArrayContract arrayContract = (JsonArrayContract) valueContract;
SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, containerContract, containerProperty);
if (!arrayContract.IsMultidimensionalArray)
SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, containerContract, containerProperty);
else
SerializeMultidimensionalArray(writer, (Array)value, arrayContract, member, containerContract, containerProperty);
break;
case JsonContractType.Primitive:
SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, containerContract, containerProperty);
@@ -322,39 +325,14 @@ namespace Newtonsoft.Json.Serialization
{
try
{
if (!property.Ignored && property.Readable && ShouldSerialize(property, value) && IsSpecified(property, value))
{
if (property.PropertyContract == null)
property.PropertyContract = Serializer.ContractResolver.ResolveContract(property.PropertyType);
object memberValue;
JsonContract memberContract;
object memberValue = property.ValueProvider.GetValue(value);
JsonContract memberContract = (property.PropertyContract.UnderlyingType.IsSealed()) ? property.PropertyContract : GetContractSafe(memberValue);
if (!CalculatePropertyValues(writer, value, contract, member, property, out memberContract, out memberValue))
continue;
if (ShouldWriteProperty(memberValue, property))
{
string propertyName = property.PropertyName;
if (ShouldWriteReference(memberValue, property, memberContract, contract, member))
{
writer.WritePropertyName(propertyName);
WriteReference(writer, memberValue);
continue;
}
if (!CheckForCircularReference(writer, memberValue, property, memberContract, contract, member))
continue;
if (memberValue == null)
{
Required resolvedRequired = property._required ?? contract.ItemRequired ?? Required.Default;
if (resolvedRequired == Required.Always)
throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
}
writer.WritePropertyName(propertyName);
SerializeValue(writer, memberValue, memberContract, property, contract, member);
}
}
writer.WritePropertyName(property.PropertyName);
SerializeValue(writer, memberValue, memberContract, property, contract, member);
}
catch (Exception ex)
{
@@ -372,6 +350,45 @@ namespace Newtonsoft.Json.Serialization
contract.InvokeOnSerialized(value, Serializer.Context);
}
private bool CalculatePropertyValues(JsonWriter writer, object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, out JsonContract memberContract, out object memberValue)
{
if (!property.Ignored && property.Readable && ShouldSerialize(property, value) && IsSpecified(property, value))
{
if (property.PropertyContract == null)
property.PropertyContract = Serializer.ContractResolver.ResolveContract(property.PropertyType);
memberValue = property.ValueProvider.GetValue(value);
memberContract = (property.PropertyContract.UnderlyingType.IsSealed()) ? property.PropertyContract : GetContractSafe(memberValue);
if (ShouldWriteProperty(memberValue, property))
{
if (ShouldWriteReference(memberValue, property, memberContract, contract, member))
{
writer.WritePropertyName(property.PropertyName);
WriteReference(writer, memberValue);
return false;
}
if (!CheckForCircularReference(writer, memberValue, property, memberContract, contract, member))
return false;
if (memberValue == null)
{
JsonObjectContract objectContract = contract as JsonObjectContract;
Required resolvedRequired = property._required ?? ((objectContract != null) ? objectContract.ItemRequired : null) ?? Required.Default;
if (resolvedRequired == Required.Always)
throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
}
return true;
}
}
memberContract = null;
memberValue = null;
return false;
}
private void WriteObjectStart(JsonWriter writer, object value, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
{
writer.WriteStartObject();
@@ -434,7 +451,7 @@ namespace Newtonsoft.Json.Serialization
_serializeStack.Add(values.UnderlyingCollection);
bool hasWrittenMetadataObject = WriteStartArray(writer, values, contract, member, collectionContract, containerProperty);
bool hasWrittenMetadataObject = WriteStartArray(writer, values.UnderlyingCollection, contract, member, collectionContract, containerProperty);
writer.WriteStartArray();
@@ -483,7 +500,78 @@ namespace Newtonsoft.Json.Serialization
contract.InvokeOnSerialized(values.UnderlyingCollection, Serializer.Context);
}
private bool WriteStartArray(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
{
contract.InvokeOnSerializing(values, Serializer.Context);
_serializeStack.Add(values);
bool hasWrittenMetadataObject = WriteStartArray(writer, values, contract, member, collectionContract, containerProperty);
SerializeMultidimensionalArray(writer, values, contract, member, writer.Top, new int[0]);
if (hasWrittenMetadataObject)
writer.WriteEndObject();
_serializeStack.RemoveAt(_serializeStack.Count - 1);
contract.InvokeOnSerialized(values, Serializer.Context);
}
private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, int initialDepth, int[] indices)
{
int dimension = indices.Length;
int[] newIndices = new int[dimension + 1];
for (int i = 0; i < dimension; i++)
{
newIndices[i] = indices[i];
}
writer.WriteStartArray();
for (int i = 0; i < values.GetLength(dimension); i++)
{
newIndices[dimension] = i;
bool isTopLevel = (newIndices.Length == values.Rank);
if (isTopLevel)
{
object value = values.GetValue(newIndices);
try
{
JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(values);
if (ShouldWriteReference(value, null, valueContract, contract, member))
{
WriteReference(writer, value);
}
else
{
if (CheckForCircularReference(writer, value, null, valueContract, contract, member))
{
SerializeValue(writer, value, valueContract, null, contract, member);
}
}
}
catch (Exception ex)
{
if (IsErrorHandled(values, contract, i, writer.ContainerPath, ex))
HandleError(writer, initialDepth);
else
throw;
}
}
else
{
SerializeMultidimensionalArray(writer, values, contract, member, initialDepth + 1, newIndices);
}
}
writer.WriteEndArray();
}
private bool WriteStartArray(JsonWriter writer, object values, JsonArrayContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
{
bool isReference = ResolveIsReference(contract, member, containerContract, containerProperty) ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays);
bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Arrays, contract, member, containerContract, containerProperty);
@@ -496,11 +584,11 @@ namespace Newtonsoft.Json.Serialization
if (isReference)
{
writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, values.UnderlyingCollection));
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, values));
}
if (includeTypeDetails)
{
WriteTypeProperty(writer, values.UnderlyingCollection.GetType());
WriteTypeProperty(writer, values.GetType());
}
writer.WritePropertyName(JsonTypeReflector.ArrayValuesPropertyName);
}
@@ -552,17 +640,55 @@ To fix this error either change the environment to be fully trusted, change the
WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty);
int initialDepth = writer.Top;
foreach (JsonProperty property in contract.Properties)
{
// only write non-dynamic properties that have an explicit attribute
if (property.HasMemberAttribute)
{
try
{
object memberValue;
JsonContract memberContract;
if (!CalculatePropertyValues(writer, value, contract, member, property, out memberContract, out memberValue))
continue;
writer.WritePropertyName(property.PropertyName);
SerializeValue(writer, memberValue, memberContract, property, contract, member);
}
catch (Exception ex)
{
if (IsErrorHandled(value, contract, property.PropertyName, writer.ContainerPath, ex))
HandleError(writer, initialDepth);
else
throw;
}
}
}
foreach (string memberName in value.GetDynamicMemberNames())
{
object memberValue;
if (DynamicUtils.TryGetMember(value, memberName, out memberValue))
if (value.TryGetMember(memberName, out memberValue))
{
string resolvedPropertyName = (contract.PropertyNameResolver != null)
? contract.PropertyNameResolver(memberName)
: memberName;
try
{
string resolvedPropertyName = (contract.PropertyNameResolver != null)
? contract.PropertyNameResolver(memberName)
: memberName;
writer.WritePropertyName(resolvedPropertyName);
SerializeValue(writer, memberValue, GetContractSafe(memberValue), null, null, member);
writer.WritePropertyName(resolvedPropertyName);
SerializeValue(writer, memberValue, GetContractSafe(memberValue), null, null, member);
}
catch (Exception ex)
{
if (IsErrorHandled(value, contract, memberName, writer.ContainerPath, ex))
HandleError(writer, initialDepth);
else
throw;
}
}
}
@@ -326,5 +326,83 @@ namespace Newtonsoft.Json.Utilities
}
return -1;
}
private static IList<int> GetDimensions(IList values)
{
IList<int> dimensions = new List<int>();
IList currentArray = values;
while (true)
{
dimensions.Add(currentArray.Count);
if (currentArray.Count == 0)
break;
object v = currentArray[0];
if (v is IList)
currentArray = (IList)v;
else
break;
}
return dimensions;
}
private static void CopyFromJaggedToMultidimensionalArray(IList values, Array multidimensionalArray, int[] indices)
{
int dimension = indices.Length;
if (dimension == multidimensionalArray.Rank)
{
multidimensionalArray.SetValue(JaggedArrayGetValue(values, indices), indices);
return;
}
int dimensionLength = multidimensionalArray.GetLength(dimension);
IList list = (IList)JaggedArrayGetValue(values, indices);
int currentValuesLength = list.Count;
if (currentValuesLength != dimensionLength)
throw new Exception("Cannot deserialize non-cubical array as multidimensional array.");
int[] newIndices = new int[dimension + 1];
for (int i = 0; i < dimension; i++)
{
newIndices[i] = indices[i];
}
for (int i = 0; i < multidimensionalArray.GetLength(dimension); i++)
{
newIndices[dimension] = i;
CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, newIndices);
}
}
private static object JaggedArrayGetValue(IList values, int[] indices)
{
IList currentList = values;
for (int i = 0; i < indices.Length; i++)
{
int index = indices[i];
if (i == indices.Length - 1)
return currentList[index];
else
currentList = (IList)currentList[index];
}
return currentList;
}
public static Array ToMultidimensionalArray(IList values, Type type, int rank)
{
IList<int> dimensions = GetDimensions(values);
while (dimensions.Count < rank)
{
dimensions.Add(0);
}
Array multidimensionalArray = Array.CreateInstance(type, dimensions.ToArray());
CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, new int[0]);
return multidimensionalArray;
}
}
}