From 6b2238c39c8365c3aafc320775952b42cc1ed302 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sat, 7 May 2011 02:16:26 +0000 Subject: [PATCH] -Added Serializable attribute to exceptions -Fixed deserializing null values into DataTables -Fixed error when deserializing readonly IEnumerable array properties -Fixed not including type name for byte[] values --- .../Converters/DataTableConverterTests.cs | 53 ++++++++++++++++++- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 19 +++++++ .../Serialization/TypeNameHandlingTests.cs | 50 +++++++++++++++++ .../Converters/DataTableConverter.cs | 2 +- Src/Newtonsoft.Json/JsonReaderException.cs | 3 ++ .../JsonSerializationException.cs | 3 ++ Src/Newtonsoft.Json/JsonWriterException.cs | 3 ++ .../Properties/AssemblyInfo.cs | 2 +- .../Schema/JsonSchemaException.cs | 3 ++ .../JsonSerializerInternalReader.cs | 21 ++++++++ .../JsonSerializerInternalWriter.cs | 21 +++++++- .../Serialization/JsonTypeReflector.cs | 1 + .../Utilities/CollectionWrapper.cs | 9 +++- 14 files changed, 185 insertions(+), 7 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs index ffa3dcb7..dcc93814 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs @@ -107,7 +107,7 @@ namespace Newtonsoft.Json.Tests.Converters { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - DataTable d = (DataTable) value; + DataTable d = (DataTable)value; writer.WriteValue(d.TableName); } @@ -121,7 +121,7 @@ namespace Newtonsoft.Json.Tests.Converters public override bool CanConvert(Type objectType) { - return (objectType == typeof (DataTable)); + return (objectType == typeof(DataTable)); } } @@ -136,6 +136,55 @@ namespace Newtonsoft.Json.Tests.Converters DataTable t2 = JsonConvert.DeserializeObject(json, new TestDataTableConverter()); Assert.AreEqual(t1.TableName, t2.TableName); } + + [Test] + public void SerializeDataTableWithNull() + { + var table = new DataTable(); + table.Columns.Add("item"); + table.Columns.Add("price", typeof(double)); + table.Rows.Add("shirt", 49.99); + table.Rows.Add("pants", 54.99); + table.Rows.Add("shoes"); // no price + + var json = JsonConvert.SerializeObject(table); + Assert.AreEqual(@"[" + + @"{""item"":""shirt"",""price"":49.99}," + + @"{""item"":""pants"",""price"":54.99}," + + @"{""item"":""shoes"",""price"":null}]", json); + } + + [Test] + public void DerializeDataTableWithImplicitNull() + { + const string json = @"[" + + @"{""item"":""shirt"",""price"":49.99}," + + @"{""item"":""pants"",""price"":54.99}," + + @"{""item"":""shoes""}]"; + var table = JsonConvert.DeserializeObject(json); + Assert.AreEqual("shirt", table.Rows[0]["item"]); + Assert.AreEqual("pants", table.Rows[1]["item"]); + Assert.AreEqual("shoes", table.Rows[2]["item"]); + Assert.AreEqual(49.99, (double)table.Rows[0]["price"], 0.01); + Assert.AreEqual(54.99, (double)table.Rows[1]["price"], 0.01); + Assert.IsInstanceOfType(typeof(System.DBNull), table.Rows[2]["price"]); + } + + [Test] + public void DerializeDataTableWithExplicitNull() + { + const string json = @"[" + + @"{""item"":""shirt"",""price"":49.99}," + + @"{""item"":""pants"",""price"":54.99}," + + @"{""item"":""shoes"",""price"":null}]"; + var table = JsonConvert.DeserializeObject(json); + Assert.AreEqual("shirt", table.Rows[0]["item"]); + Assert.AreEqual("pants", table.Rows[1]["item"]); + Assert.AreEqual("shoes", table.Rows[2]["item"]); + Assert.AreEqual(49.99, (double)table.Rows[0]["price"], 0.01); + Assert.AreEqual(54.99, (double)table.Rows[1]["price"], 0.01); + Assert.IsInstanceOfType(typeof(System.DBNull), table.Rows[2]["price"]); + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 89879d42..989d5e9d 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -41,5 +41,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13702")] +[assembly: AssemblyFileVersion("4.0.2.13707")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 634dd714..73c50a00 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -4310,5 +4310,24 @@ keyword such as type of business."" var withParameterisedConstructor = JsonConvert.DeserializeObject(json); Assert.AreEqual(withoutParameterisedConstructor.B, withParameterisedConstructor.B); } + + public class EnumerableArrayPropertyClass + { + public IEnumerable Numbers + { + get + { + return new[] { 1, 2, 3 }; //fails + //return new List(new[] { 1, 2, 3 }); //works + } + } + } + + [Test] + public void SkipPopulatingArrayPropertyClass() + { + string json = JsonConvert.SerializeObject(new EnumerableArrayPropertyClass()); + JsonConvert.DeserializeObject(json); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index 715b939b..198645c7 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -703,6 +703,56 @@ namespace Newtonsoft.Json.Tests.Serialization } } + public class Car + { + // included in JSON + public string Model { get; set; } + public DateTime Year { get; set; } + public List Features { get; set; } + public object[] Objects { get; set; } + + // ignored + [JsonIgnore] + public DateTime LastModified { get; set; } + } + + [Test] + public void ByteArrays() + { + Car testerObject = new Car(); + byte[] data = new byte[] {75, 65, 82, 73, 82, 65}; + testerObject.Objects = new object[] { data, "prueba" }; + + JsonSerializerSettings jsonSettings = new JsonSerializerSettings(); + jsonSettings.NullValueHandling = NullValueHandling.Ignore; + jsonSettings.TypeNameHandling = TypeNameHandling.All; + + string output = JsonConvert.SerializeObject(testerObject, Formatting.Indented, jsonSettings); + + string carClassRef = ReflectionUtils.GetTypeName(typeof(Car), FormatterAssemblyStyle.Simple); + + Assert.AreEqual(output, @"{ + ""$type"": """ + carClassRef + @""", + ""Year"": ""\/Date(-62135596800000+1300)\/"", + ""Objects"": { + ""$type"": ""System.Object[], mscorlib"", + ""$values"": [ + { + ""$type"": ""System.Byte[], mscorlib"", + ""$value"": ""S0FSSVJB"" + }, + ""prueba"" + ] + } +}"); + Car obj = JsonConvert.DeserializeObject(output, jsonSettings); + + Assert.IsNotNull(obj); + + Assert.IsTrue(obj.Objects[0] is byte[]); + + Assert.AreEqual(data, obj.Objects[0]); + } } public class Message diff --git a/Src/Newtonsoft.Json/Converters/DataTableConverter.cs b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs index 0eb61801..5ea4c5fa 100644 --- a/Src/Newtonsoft.Json/Converters/DataTableConverter.cs +++ b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs @@ -101,7 +101,7 @@ namespace Newtonsoft.Json.Converters dt.Columns.Add(new DataColumn(columnName, columnType)); } - dr[columnName] = reader.Value; + dr[columnName] = reader.Value ?? DBNull.Value; reader.Read(); } diff --git a/Src/Newtonsoft.Json/JsonReaderException.cs b/Src/Newtonsoft.Json/JsonReaderException.cs index 78ac8912..5c3159bb 100644 --- a/Src/Newtonsoft.Json/JsonReaderException.cs +++ b/Src/Newtonsoft.Json/JsonReaderException.cs @@ -30,6 +30,9 @@ namespace Newtonsoft.Json /// /// The exception thrown when an error occurs while reading Json text. /// +#if (!SILVERLIGHT && !WINDOWS_PHONE) + [Serializable] +#endif public class JsonReaderException : Exception { /// diff --git a/Src/Newtonsoft.Json/JsonSerializationException.cs b/Src/Newtonsoft.Json/JsonSerializationException.cs index bec26350..687f4617 100644 --- a/Src/Newtonsoft.Json/JsonSerializationException.cs +++ b/Src/Newtonsoft.Json/JsonSerializationException.cs @@ -32,6 +32,9 @@ namespace Newtonsoft.Json /// /// The exception thrown when an error occurs during Json serialization or deserialization. /// +#if (!SILVERLIGHT && !WINDOWS_PHONE) + [Serializable] +#endif public class JsonSerializationException : Exception { /// diff --git a/Src/Newtonsoft.Json/JsonWriterException.cs b/Src/Newtonsoft.Json/JsonWriterException.cs index bc43cd37..f08e84c1 100644 --- a/Src/Newtonsoft.Json/JsonWriterException.cs +++ b/Src/Newtonsoft.Json/JsonWriterException.cs @@ -32,6 +32,9 @@ namespace Newtonsoft.Json /// /// The exception thrown when an error occurs while reading Json text. /// +#if (!SILVERLIGHT && !WINDOWS_PHONE) + [Serializable] +#endif public class JsonWriterException : Exception { /// diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index c67da98a..c78177af 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -110,7 +110,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13702")] +[assembly: AssemblyFileVersion("4.0.2.13707")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs index a0b65e01..b9f296c7 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs @@ -30,6 +30,9 @@ namespace Newtonsoft.Json.Schema /// /// Returns detailed information about the schema exception. /// +#if (!SILVERLIGHT && !WINDOWS_PHONE) + [Serializable] +#endif public class JsonSchemaException : Exception { /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index a94da640..dc3cc501 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -399,6 +399,20 @@ namespace Newtonsoft.Json.Serialization return PopulateObject(existingValue, reader, objectContract, id); } + JsonPrimitiveContract primitiveContract = contract as JsonPrimitiveContract; + if (primitiveContract != null) + { + // if the content is inside $value then read past it + if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.ValuePropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + object value = CreateValueInternal(reader, objectType, primitiveContract, member, existingValue); + + CheckedRead(reader); + return value; + } + } + #if !SILVERLIGHT && !PocketPC JsonISerializableContract serializableContract = contract as JsonISerializableContract; if (serializableContract != null) @@ -688,6 +702,13 @@ namespace Newtonsoft.Json.Serialization { object list = wrappedList.UnderlyingCollection; + // can't populate an existing array + if (wrappedList.IsFixedSize) + { + reader.Skip(); + return wrappedList.UnderlyingCollection; + } + if (reference != null) Serializer.ReferenceResolver.AddReference(this, reference, list); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 2c89c2f4..fa3d0a33 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -86,6 +86,25 @@ namespace Newtonsoft.Json.Serialization return Serializer.ContractResolver.ResolveContract(value.GetType()); } + private void SerializePrimitive(JsonWriter writer, object value, JsonPrimitiveContract contract, JsonProperty member, JsonContract collectionValueContract) + { + if (contract.UnderlyingType == typeof (byte[])) + { + bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionValueContract); + if (includeTypeDetails) + { + writer.WriteStartObject(); + WriteTypeProperty(writer, contract.CreatedType); + writer.WritePropertyName(JsonTypeReflector.ValuePropertyName); + writer.WriteValue(value); + writer.WriteEndObject(); + return; + } + } + + writer.WriteValue(value); + } + private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContract collectionValueContract) { JsonConverter converter = (member != null) ? member.Converter : null; @@ -106,7 +125,7 @@ namespace Newtonsoft.Json.Serialization } else if (valueContract is JsonPrimitiveContract) { - writer.WriteValue(value); + SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, collectionValueContract); } else if (valueContract is JsonStringContract) { diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index b9f64932..b53a9f16 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -46,6 +46,7 @@ namespace Newtonsoft.Json.Serialization public const string IdPropertyName = "$id"; public const string RefPropertyName = "$ref"; public const string TypePropertyName = "$type"; + public const string ValuePropertyName = "$value"; public const string ArrayValuesPropertyName = "$values"; public const string ShouldSerializePrefix = "ShouldSerialize"; diff --git a/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs b/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs index bdd09318..4d088404 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs @@ -194,7 +194,14 @@ namespace Newtonsoft.Json.Utilities bool IList.IsFixedSize { - get { return false; } + get + { + if (_genericCollection != null) + // ICollection only has IsReadOnly + return _genericCollection.IsReadOnly; + else + return _list.IsFixedSize; + } } void IList.Remove(object value)