From f9fa0b00e7d73b54622aa9815e13f5080e9af85a Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 23 Feb 2013 22:09:51 -0800 Subject: [PATCH] -Added DeserializeAnonymousType overload with JsonSerializerSettings -Added support for specifying the root type when serialize JSON to use with TypeNameHandling.Auto -Fixed recursively reading type wrapper objects when deserializing --- .../JsonTextReaderTest.cs | 30 +++++++++ .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 24 +++++++ .../Serialization/TypeNameHandlingTests.cs | 63 +++++++++++++++++++ Src/Newtonsoft.Json/JsonConvert.cs | 22 ++++++- Src/Newtonsoft.Json/JsonSerializer.cs | 21 ++++++- .../Properties/AssemblyInfo.cs | 2 +- .../JsonSerializerInternalReader.cs | 8 ++- .../JsonSerializerInternalWriter.cs | 11 +++- .../Serialization/JsonSerializerProxy.cs | 4 +- 10 files changed, 177 insertions(+), 10 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index c081a719..548990b4 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -1266,6 +1266,36 @@ bye", reader.Value); Assert.IsFalse(jsonReader.Read()); } + [Test] + public void ReadOctalNumberAsInt64() + { + StringReader s = new StringReader(@"[0372, 0xFA, 0XFA]"); + JsonTextReader jsonReader = new JsonTextReader(s); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.StartArray, jsonReader.TokenType); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(typeof(long), jsonReader.ValueType); + Assert.AreEqual(250, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(typeof(long), jsonReader.ValueType); + Assert.AreEqual(250, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(typeof(long), jsonReader.ValueType); + Assert.AreEqual(250, jsonReader.Value); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.EndArray, jsonReader.TokenType); + + Assert.IsFalse(jsonReader.Read()); + } + [Test] public void ReadOctalNumberAsInt32() { diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 33c4eed9..dea60e6e 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -73,4 +73,4 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("4.5.11.15814")] +[assembly: AssemblyFileVersion("4.5.11.15823")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 58e143a0..e36c4de2 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -580,6 +580,30 @@ keyword such as type of business."" Assert.AreEqual("TestProduct", anonymous.Product.Name); } + [Test] + public void AnonymousObjectSerializationWithSetting() + { + DateTime d = new DateTime(2000, 1, 1); + + var anonymous = + new + { + DateValue = d + }; + + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.Converters.Add(new IsoDateTimeConverter + { + DateTimeFormat = "yyyy" + }); + + string json = JsonConvert.SerializeObject(anonymous, settings); + Assert.AreEqual(@"{""DateValue"":""2000""}", json); + + anonymous = JsonConvert.DeserializeAnonymousType(json, anonymous, settings); + Assert.AreEqual(d, anonymous.DateValue); + } + [Test] public void CustomCollectionSerialization() { diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index e05d3b71..e161639a 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -27,6 +27,7 @@ #if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE) using System.Dynamic; #endif +using System.Text; using Newtonsoft.Json.Tests.Linq; using global::System; using global::System.Collections; @@ -53,6 +54,68 @@ namespace Newtonsoft.Json.Tests.Serialization [TestFixture] public class TypeNameHandlingTests : TestFixtureBase { + [Test] + public void NestedValueObjects() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10000; i++) + { + sb.Append(@"{""$value"":"); + } + + ExceptionAssert.Throws("Unexpected token when deserializing primitive value: StartObject. Path '$value', line 1, position 11.", () => + { + var reader = new JsonTextReader(new StringReader(sb.ToString())); + var ser = new JsonSerializer(); + ser.Deserialize(reader); + }); + } + + [Test] + public void SerializeRootTypeNameIfDerivedWithAuto() + { + var serializer = new JsonSerializer() + { + TypeNameHandling = TypeNameHandling.Auto + }; + var sw = new StringWriter(); + serializer.Serialize(new JsonTextWriter(sw) { Formatting = Formatting.Indented }, new WagePerson()); + var result = sw.ToString(); + + Assert.AreEqual(@"{ + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"", + ""HourlyWage"": 0.0, + ""Name"": null, + ""BirthDate"": ""0001-01-01T00:00:00"", + ""LastModified"": ""0001-01-01T00:00:00"" +}", result); + + Assert.IsTrue(result.Contains("WagePerson")); + using (var rd = new JsonTextReader(new StringReader(result))) + { + var person = serializer.Deserialize(rd); + + CustomAssert.IsInstanceOfType(typeof(WagePerson), person); + } + } + + [Test] + public void SerializeRootTypeNameAutoWithJsonConvert() + { + string json = JsonConvert.SerializeObject(new WagePerson(), Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }); + + Assert.AreEqual(@"{ + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"", + ""HourlyWage"": 0.0, + ""Name"": null, + ""BirthDate"": ""0001-01-01T00:00:00"", + ""LastModified"": ""0001-01-01T00:00:00"" +}", json); + } + public class Wrapper { public IList Array { get; set; } diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 923946ee..d4f06ca9 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -792,6 +792,16 @@ namespace Newtonsoft.Json return SerializeObject(value, Formatting.None, settings); } + public static string SerializeObject(T value, JsonSerializerSettings settings) + { + return SerializeObject(value, Formatting.None, settings); + } + + public static string SerializeObject(T value, Formatting formatting, JsonSerializerSettings settings) + { + return SerializeObjectInternal(value, formatting, settings, typeof (T)); + } + /// /// Serializes the specified object to a JSON string using a collection of . /// @@ -803,6 +813,11 @@ namespace Newtonsoft.Json /// A JSON string representation of the object. /// public static string SerializeObject(object value, Formatting formatting, JsonSerializerSettings settings) + { + return SerializeObjectInternal(value, formatting, settings, null); + } + + public static string SerializeObjectInternal(object value, Formatting formatting, JsonSerializerSettings settings, Type rootType) { JsonSerializer jsonSerializer = JsonSerializer.Create(settings); @@ -812,7 +827,7 @@ namespace Newtonsoft.Json { jsonWriter.Formatting = formatting; - jsonSerializer.Serialize(jsonWriter, value); + jsonSerializer.Serialize(jsonWriter, value, rootType); } return sw.ToString(); @@ -924,6 +939,11 @@ namespace Newtonsoft.Json return DeserializeObject(value); } + public static T DeserializeAnonymousType(string value, T anonymousTypeObject, JsonSerializerSettings settings) + { + return DeserializeObject(value, settings); + } + /// /// Deserializes the JSON to the specified .NET type. /// diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 72bd98a5..9e661635 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -632,6 +632,16 @@ namespace Newtonsoft.Json Serialize(new JsonTextWriter(textWriter), value); } + public void Serialize(JsonWriter jsonWriter, object value, Type rootType) + { + SerializeInternal(jsonWriter, value, rootType); + } + + public void Serialize(TextWriter textWriter, object value, Type rootType) + { + Serialize(new JsonTextWriter(textWriter), value, rootType); + } + /// /// Serializes the specified and writes the Json structure /// to a Stream using the specified . @@ -640,10 +650,15 @@ namespace Newtonsoft.Json /// The to serialize. public void Serialize(JsonWriter jsonWriter, object value) { - SerializeInternal(jsonWriter, value); + SerializeInternal(jsonWriter, value, null); } - internal virtual void SerializeInternal(JsonWriter jsonWriter, object value) + public void Serialize(JsonWriter jsonWriter, object value) + { + SerializeInternal(jsonWriter, value, typeof(T)); + } + + internal virtual void SerializeInternal(JsonWriter jsonWriter, object value, Type rootType) { ValidationUtils.ArgumentNotNull(jsonWriter, "jsonWriter"); @@ -698,7 +713,7 @@ namespace Newtonsoft.Json } JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(this); - serializerWriter.Serialize(jsonWriter, value); + serializerWriter.Serialize(jsonWriter, value, rootType); // reset writer back to previous options if (previousFormatting != null) diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 9878a2f8..8d58b8ab 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -87,6 +87,6 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("4.5.11.15814")] +[assembly: AssemblyFileVersion("4.5.11.15823")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index c718d0bd..ffcb3604 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -367,11 +367,17 @@ namespace Newtonsoft.Json.Serialization return PopulateObject(targetObject, reader, objectContract, member, id); case JsonContractType.Primitive: - JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract) contract; + JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract; // 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); + + // the token should not be an object because the $type value could have been included in the object + // without needing the $value property + if (reader.TokenType == JsonToken.StartObject) + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing primitive value: " + reader.TokenType); + object value = CreateValueInternal(reader, objectType, primitiveContract, member, null, null, existingValue); CheckedRead(reader); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 7e25bbe6..c1530ac1 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -46,6 +46,7 @@ namespace Newtonsoft.Json.Serialization { internal class JsonSerializerInternalWriter : JsonSerializerInternalBase { + private JsonContract _rootContract; private readonly List _serializeStack = new List(); private JsonSerializerProxy _internalSerializer; @@ -54,11 +55,14 @@ namespace Newtonsoft.Json.Serialization { } - public void Serialize(JsonWriter jsonWriter, object value) + public void Serialize(JsonWriter jsonWriter, object value, Type rootType) { if (jsonWriter == null) throw new ArgumentNullException("jsonWriter"); + if (rootType != null) + _rootContract = Serializer.ContractResolver.ResolveContract(rootType); + SerializeValue(jsonWriter, value, GetContractSafe(value), null, null, null); } @@ -802,6 +806,11 @@ To fix this error either change the environment to be fully trusted, change the if (contract.UnderlyingType != containerContract.ItemContract.CreatedType) return true; } + else if (_rootContract != null && _serializeStack.Count == 1) + { + if (contract.UnderlyingType != _rootContract.CreatedType) + return true; + } } return false; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs index a7fff0d0..c616215c 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -232,10 +232,10 @@ namespace Newtonsoft.Json.Serialization _serializer.Populate(reader, target); } - internal override void SerializeInternal(JsonWriter jsonWriter, object value) + internal override void SerializeInternal(JsonWriter jsonWriter, object value, Type rootType) { if (_serializerWriter != null) - _serializerWriter.Serialize(jsonWriter, value); + _serializerWriter.Serialize(jsonWriter, value, rootType); else _serializer.Serialize(jsonWriter, value); }