From 0f77cd1b0313f5face3884234ffc3a1c8af4ac4c Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 12 Feb 2014 01:43:46 +1300 Subject: [PATCH 1/3] -Added SpecialPropertiesHandling --- .../Linq/JTokenReaderTest.cs | 15 ++ .../Linq/LinqToJsonTest.cs | 18 ++ .../Newtonsoft.Json.Tests.Net20.csproj | 1 + .../Newtonsoft.Json.Tests.Net35.csproj | 1 + .../Newtonsoft.Json.Tests.Net40.csproj | 1 + .../Newtonsoft.Json.Tests.Portable.csproj | 1 + .../Newtonsoft.Json.Tests.Portable40.csproj | 1 + .../Newtonsoft.Json.Tests.csproj | 1 + .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 143 ++++++++---- .../SerializationErrorHandlingTests.cs | 4 +- .../SpecialPropertyHandlingTests.cs | 209 ++++++++++++++++++ .../Serialization/TraceWriterTests.cs | 35 +-- .../Serialization/TypeNameHandlingTests.cs | 2 + Src/Newtonsoft.Json/JsonReader.cs | 32 +-- Src/Newtonsoft.Json/JsonSerializer.cs | 20 ++ Src/Newtonsoft.Json/JsonSerializerSettings.cs | 12 + Src/Newtonsoft.Json/Linq/JContainer.cs | 2 +- Src/Newtonsoft.Json/Linq/JToken.cs | 8 +- Src/Newtonsoft.Json/Linq/JTokenReader.cs | 36 ++- .../Newtonsoft.Json.Net20.csproj | 1 + .../Newtonsoft.Json.Net35.csproj | 1 + .../Newtonsoft.Json.Net40.csproj | 1 + .../Newtonsoft.Json.Portable.csproj | 1 + .../Newtonsoft.Json.Portable40.csproj | 1 + Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 1 + .../Properties/AssemblyInfo.cs | 2 +- .../JsonSerializerInternalReader.cs | 182 +++++++++++---- .../Serialization/JsonSerializerProxy.cs | 6 + .../SpecialPropertyHandling.cs | 47 ++++ 30 files changed, 659 insertions(+), 128 deletions(-) create mode 100644 Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs create mode 100644 Src/Newtonsoft.Json/SpecialPropertyHandling.cs diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs index dcb71a58..d9a94004 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs @@ -44,6 +44,21 @@ namespace Newtonsoft.Json.Tests.Linq [TestFixture] public class JTokenReaderTest : TestFixtureBase { + [Test] + public void ErrorTokenIndex() + { + JObject json = JObject.Parse(@"{""IntList"":[1, ""two""]}"); + + ExceptionAssert.Throws( + "Could not convert string to integer: two. Path 'IntList[1]', line 1, position 20.", + () => + { + JsonSerializer serializer = new JsonSerializer(); + + serializer.Deserialize(json.CreateReader()); + }); + } + #if !NET20 [Test] public void YahooFinance() diff --git a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs index 6644f276..ac5000e5 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -49,6 +49,24 @@ namespace Newtonsoft.Json.Tests.Linq [TestFixture] public class LinqToJsonTest : TestFixtureBase { + [Test] + public void JPropertyPath() + { + JObject o = new JObject + { + { + "person", + new JObject + { + { "$id", 1 } + } + } + }; + + JContainer idProperty = o["person"]["$id"].Parent; + Assert.AreEqual("person.$id", idProperty.Path); + } + [Test] public void ForEach() { diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 92932bc1..bd3693d7 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -102,6 +102,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index 8b370b01..313dc6e9 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -144,6 +144,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj index f1d7da90..e137e0fd 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj @@ -260,6 +260,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj index d3b4c024..42f7caa6 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj @@ -144,6 +144,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj index f48311e0..9ac4fd92 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj @@ -139,6 +139,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 3abf477c..02e75a34 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -243,6 +243,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index ed21e90e..24837880 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.1.16929")] +[assembly: AssemblyFileVersion("6.0.1.17012")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 5a654f6d..b939a1fb 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -2345,19 +2345,22 @@ keyword such as type of business."" [Test] public void DeserializeRequiredMembersClassNullRequiredValueProperty() { - ExceptionAssert.Throws( - "Required property 'FirstName' expects a value but got null. Path '', line 6, position 2.", - () => - { - string json = @"{ + try + { + string json = @"{ ""FirstName"": null, ""MiddleName"": null, ""LastName"": null, ""BirthDate"": ""\/Date(977309755000)\/"" }"; - JsonConvert.DeserializeObject(json); - }); + JsonConvert.DeserializeObject(json); + Assert.Fail(); + } + catch (JsonSerializationException ex) + { + Assert.IsTrue(ex.Message.StartsWith("Required property 'FirstName' expects a value but got null. Path ''")); + } } [Test] @@ -2383,16 +2386,19 @@ keyword such as type of business."" [Test] public void RequiredMembersClassMissingRequiredProperty() { - ExceptionAssert.Throws( - "Required property 'LastName' not found in JSON. Path '', line 3, position 2.", - () => - { - string json = @"{ + try + { + string json = @"{ ""FirstName"": ""Bob"" }"; - JsonConvert.DeserializeObject(json); - }); + JsonConvert.DeserializeObject(json); + Assert.Fail(); + } + catch (JsonSerializationException ex) + { + Assert.IsTrue(ex.Message.StartsWith("Required property 'LastName' not found in JSON. Path ''")); + } } [Test] @@ -2631,10 +2637,17 @@ keyword such as type of business."" [Test] public void RequiredWhenUsingConstructor() { - string json = "{ 'testProperty1': 'value' }"; + try + { + string json = "{ 'testProperty1': 'value' }"; + JsonConvert.DeserializeObject(json); - ExceptionAssert.Throws("Required property 'TestProperty2' not found in JSON. Path '', line 1, position 28.", - () => JsonConvert.DeserializeObject(json)); + Assert.Fail(); + } + catch (JsonSerializationException ex) + { + Assert.IsTrue(ex.Message.StartsWith("Required property 'TestProperty2' not found in JSON. Path ''")); + } } [Test] @@ -3351,11 +3364,17 @@ Path '', line 1, position 1.", { string json = @"{}"; - ExceptionAssert.Throws( - @"Cannot deserialize the current JSON object (e.g. {""name"":""value""}) into type 'System.Collections.Generic.List`1[Newtonsoft.Json.Tests.TestObjects.Person]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. + try + { + JsonConvert.DeserializeObject>(json); + Assert.Fail(); + } + catch (JsonSerializationException ex) + { + Assert.IsTrue(ex.Message.StartsWith(@"Cannot deserialize the current JSON object (e.g. {""name"":""value""}) into type 'System.Collections.Generic.List`1[Newtonsoft.Json.Tests.TestObjects.Person]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. -Path '', line 1, position 2.", - () => { JsonConvert.DeserializeObject>(json); }); +Path ''")); + } } [Test] @@ -3574,17 +3593,21 @@ Path '', line 1, position 2.", [Test] public void DeserializePersonKeyedDictionary() { - ExceptionAssert.Throws("Could not convert string 'Newtonsoft.Json.Tests.TestObjects.Person' to dictionary key type 'Newtonsoft.Json.Tests.TestObjects.Person'. Create a TypeConverter to convert from the string to the key type object. Path 'Newtonsoft.Json.Tests.TestObjects.Person', line 2, position 46.", - () => - { - string json = - @"{ + try + { + string json = + @"{ ""Newtonsoft.Json.Tests.TestObjects.Person"": 1, ""Newtonsoft.Json.Tests.TestObjects.Person"": 2 }"; - JsonConvert.DeserializeObject>(json); - }); + JsonConvert.DeserializeObject>(json); + Assert.Fail(); + } + catch (JsonSerializationException ex) + { + Assert.IsTrue(ex.Message.StartsWith("Could not convert string 'Newtonsoft.Json.Tests.TestObjects.Person' to dictionary key type 'Newtonsoft.Json.Tests.TestObjects.Person'. Create a TypeConverter to convert from the string to the key type object. Path 'Newtonsoft.Json.Tests.TestObjects.Person'")); + } } [Test] @@ -5004,7 +5027,10 @@ To fix this error either change the environment to be fully trusted, change the child.Add("Name", "Isabell"); child.Add("Father", reference); - var json = JsonConvert.SerializeObject(child); + string json = JsonConvert.SerializeObject(child); + + Console.WriteLine(json); + Dictionary result = JsonConvert.DeserializeObject>(json); Assert.AreEqual(3, result.Count); @@ -5447,7 +5473,14 @@ To fix this error either change the environment to be fully trusted, change the }); Assert.AreEqual("{\"Offset\":\"2000-01-01T00:00:00+06:00\"}", serializeObject); - var deserializeObject = JsonConvert.DeserializeObject(serializeObject); + + JsonTextReader reader = new JsonTextReader(new StringReader(serializeObject)); + reader.DateParseHandling = DateParseHandling.None; + + JsonSerializer serializer = new JsonSerializer(); + + var deserializeObject = serializer.Deserialize(reader); + Assert.AreEqual(TimeSpan.FromHours(6), deserializeObject.Offset.Offset); Assert.AreEqual(new DateTime(2000, 1, 1), deserializeObject.Offset.Date); } @@ -5468,7 +5501,13 @@ To fix this error either change the environment to be fully trusted, change the }, Formatting.None, new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat }); Assert.AreEqual("{\"Offset\":\"\\/Date(946663200000+0600)\\/\"}", serializeObject); - var deserializeObject = JsonConvert.DeserializeObject(serializeObject); + + JsonTextReader reader = new JsonTextReader(new StringReader(serializeObject)); + + JsonSerializer serializer = new JsonSerializer(); + + var deserializeObject = serializer.Deserialize(reader); + Assert.AreEqual(TimeSpan.FromHours(6), deserializeObject.Offset.Offset); Assert.AreEqual(new DateTime(2000, 1, 1), deserializeObject.Offset.Date); } @@ -5563,7 +5602,12 @@ To fix this error either change the environment to be fully trusted, change the public void DeserializeDecimalPropertyExact() { string json = "{Amount:123456789876543.21}"; - Invoice i = JsonConvert.DeserializeObject(json); + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.FloatParseHandling = FloatParseHandling.Decimal; + + JsonSerializer serializer = new JsonSerializer(); + + Invoice i = serializer.Deserialize(reader); Assert.AreEqual(123456789876543.21m, i.Amount); } @@ -5579,7 +5623,12 @@ To fix this error either change the environment to be fully trusted, change the public void DeserializeDecimalDictionaryExact() { string json = "{'Value':123456789876543.21}"; - IDictionary d = JsonConvert.DeserializeObject>(json); + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.FloatParseHandling = FloatParseHandling.Decimal; + + JsonSerializer serializer = new JsonSerializer(); + + IDictionary d = serializer.Deserialize>(reader); Assert.AreEqual(123456789876543.21m, d["Value"]); } @@ -5794,7 +5843,7 @@ To fix this error either change the environment to be fully trusted, change the } [Test] - public void OnError() + public void DeserializeMinValueDecimal() { var data = new DecimalTest(decimal.MinValue); var json = JsonConvert.SerializeObject(data); @@ -6957,8 +7006,8 @@ To fix this error either change the environment to be fully trusted, change the [Test] public void DeserializeUnexpectedEndInt() { - ExceptionAssert.Throws( - "Unexpected end when setting PreProperty's value. Path 'PreProperty', line 2, position 18.", + ExceptionAssert.Throws( + null, () => { string json = @"{ @@ -7395,10 +7444,10 @@ Parameter name: value", Assert.IsNotNull(o); Assert.AreEqual(4, errors.Count); - Assert.AreEqual("Required property 'NonAttributeProperty' not found in JSON. Path '', line 1, position 2.", errors[0]); - Assert.AreEqual("Required property 'UnsetProperty' not found in JSON. Path '', line 1, position 2.", errors[1]); - Assert.AreEqual("Required property 'AllowNullProperty' not found in JSON. Path '', line 1, position 2.", errors[2]); - Assert.AreEqual("Required property 'AlwaysProperty' not found in JSON. Path '', line 1, position 2.", errors[3]); + Assert.IsTrue(errors[0].StartsWith("Required property 'NonAttributeProperty' not found in JSON. Path ''")); + Assert.IsTrue(errors[1].StartsWith("Required property 'UnsetProperty' not found in JSON. Path ''")); + Assert.IsTrue(errors[2].StartsWith("Required property 'AllowNullProperty' not found in JSON. Path ''")); + Assert.IsTrue(errors[3].StartsWith("Required property 'AlwaysProperty' not found in JSON. Path ''")); } [Test] @@ -7420,9 +7469,9 @@ Parameter name: value", Assert.IsNotNull(o); Assert.AreEqual(3, errors.Count); - Assert.AreEqual("Required property 'NonAttributeProperty' expects a value but got null. Path '', line 1, position 97.", errors[0]); - Assert.AreEqual("Required property 'UnsetProperty' expects a value but got null. Path '', line 1, position 97.", errors[1]); - Assert.AreEqual("Required property 'AlwaysProperty' expects a value but got null. Path '', line 1, position 97.", errors[2]); + Assert.IsTrue(errors[0].StartsWith("Required property 'NonAttributeProperty' expects a value but got null. Path ''")); + Assert.IsTrue(errors[1].StartsWith("Required property 'UnsetProperty' expects a value but got null. Path ''")); + Assert.IsTrue(errors[2].StartsWith("Required property 'AlwaysProperty' expects a value but got null. Path ''")); } [Test] @@ -8552,8 +8601,8 @@ Parameter name: value", { string json = @"{""Before"":""Before!"",""Coordinates"":[/*hi*/[/*hi*/[1/*hi*/,/*hi*/1/*hi*/,1]/*hi*/,/*hi*/[1,1"; - ExceptionAssert.Throws( - "Unexpected end when deserializing array. Path 'Coordinates[0][1][1]', line 1, position 90.", + ExceptionAssert.Throws( + null, () => JsonConvert.DeserializeObject(json)); } @@ -8562,8 +8611,8 @@ Parameter name: value", { string json = @"{""Before"":""Before!"",""Coordinates"":[/*hi*/[/*hi*/"; - ExceptionAssert.Throws( - "Unexpected end when deserializing array. Path 'Coordinates[0]', line 1, position 48.", + ExceptionAssert.Throws( + null, () => JsonConvert.DeserializeObject(json)); } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index 9fb267b8..fc607370 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -415,8 +415,8 @@ namespace Newtonsoft.Json.Tests.Serialization serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof(MyTypeWithRequiredMembers)); Assert.AreEqual(2, errors.Count); - Assert.AreEqual(" - Required1 - Required property 'Required1' not found in JSON. Path '', line 1, position 2.", errors[0]); - Assert.AreEqual(" - Required2 - Required property 'Required2' not found in JSON. Path '', line 1, position 2.", errors[1]); + Assert.IsTrue(errors[0].StartsWith(" - Required1 - Required property 'Required1' not found in JSON. Path '', line 1, position 2.")); + Assert.IsTrue(errors[1].StartsWith(" - Required2 - Required property 'Required2' not found in JSON. Path '', line 1, position 2.")); } [Test] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs new file mode 100644 index 00000000..a092a576 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization.Formatters; +using System.Text; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + [TestFixture] + public class SpecialPropertyHandlingTests : TestFixtureBase + { + [Test] + public void DeserializeArraysWithPreserveObjectReferences() + { + string json = @"{ + ""$id"": ""1"", + ""$values"": [ + null, + { + ""$id"": ""2"", + ""$values"": [ + null + ] + }, + { + ""$id"": ""3"", + ""$values"": [ + { + ""$id"": ""4"", + ""$values"": [ + { + ""$ref"": ""1"" + } + ] + } + ] + } + ] +}"; + + ExceptionAssert.Throws( + @"Cannot preserve reference to array or readonly list, or list created from a non-default constructor: System.String[][]. Path '$values', line 3, position 15.", + () => + { + JsonConvert.DeserializeObject(json, + new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + }); + } + + [Test] + public void SerializeRefNull() + { + var reference = new Dictionary(); + reference.Add("blah", "blah!"); + reference.Add("$ref", null); + reference.Add("$id", null); + + var child = new Dictionary(); + child.Add("_id", 2); + child.Add("Name", "Isabell"); + child.Add("Father", reference); + + string json = JsonConvert.SerializeObject(child); + + Console.WriteLine(json); + + Dictionary result = JsonConvert.DeserializeObject>(json, new JsonSerializerSettings + { + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + + Assert.AreEqual(3, result.Count); + Assert.AreEqual(1, ((JObject)result["Father"]).Count); + Assert.AreEqual("blah!", (string)((JObject)result["Father"])["blah"]); + } + + [Test] + public void DeserializeEmployeeReference() + { + string json = @"[ + { + ""Name"": ""Mike Manager"", + ""$id"": ""1"", + ""Manager"": null + }, + { + ""Name"": ""Joe User"", + ""$id"": ""2"", + ""Manager"": { + ""$ref"": ""1"" + } + } +]"; + + List employees = JsonConvert.DeserializeObject>(json, new JsonSerializerSettings + { + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + + Assert.AreEqual(2, employees.Count); + Assert.AreEqual("Mike Manager", employees[0].Name); + Assert.AreEqual("Joe User", employees[1].Name); + Assert.AreEqual(employees[0], employees[1].Manager); + } + +#if !(PORTABLE || PORTABLE40) + [Test] + public void DeserializeGenericObjectListWithTypeName() + { + string employeeRef = typeof(EmployeeReference).AssemblyQualifiedName; + string personRef = typeof(Person).AssemblyQualifiedName; + + string json = @"[ + { + ""Name"": ""Bob"", + ""$id"": ""1"", + ""$type"": """ + employeeRef + @""", + ""Manager"": { + ""$id"": ""2"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Frank"", + ""Manager"": null + } + }, + { + ""Name"": null, + ""$type"": """ + personRef + @""", + ""BirthDate"": ""\/Date(978134400000)\/"", + ""LastModified"": ""\/Date(978134400000)\/"" + }, + ""String!"", + -2147483648 +]"; + + List values = (List)JsonConvert.DeserializeObject(json, typeof(List), new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full, + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + + Assert.AreEqual(4, values.Count); + + EmployeeReference e = (EmployeeReference)values[0]; + Person p = (Person)values[1]; + + Assert.AreEqual("Bob", e.Name); + Assert.AreEqual("Frank", e.Manager.Name); + + Assert.AreEqual(null, p.Name); + Assert.AreEqual(new DateTime(2000, 12, 30, 0, 0, 0, DateTimeKind.Utc), p.BirthDate); + Assert.AreEqual(new DateTime(2000, 12, 30, 0, 0, 0, DateTimeKind.Utc), p.LastModified); + + Assert.AreEqual("String!", values[2]); + Assert.AreEqual((long)int.MinValue, values[3]); + } + + [Test] + public void WriteListTypeNameForProperty() + { + string listRef = ReflectionUtils.GetTypeName(typeof(List), FormatterAssemblyStyle.Simple, null); + + TypeNameHandlingTests.TypeNameProperty typeNameProperty = new TypeNameHandlingTests.TypeNameProperty + { + Name = "Name!", + Value = new List { 1, 2, 3, 4, 5 } + }; + + string json = JsonConvert.SerializeObject(typeNameProperty, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""Value"": { + ""$type"": """ + listRef + @""", + ""$values"": [ + 1, + 2, + 3, + 4, + 5 + ] + } +}", json); + + TypeNameHandlingTests.TypeNameProperty deserialized = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + Assert.AreEqual("Name!", deserialized.Name); + CustomAssert.IsInstanceOfType(typeof(List), deserialized.Value); + + List nested = (List)deserialized.Value; + Assert.AreEqual(5, nested.Count); + Assert.AreEqual(1, nested[0]); + Assert.AreEqual(2, nested[1]); + Assert.AreEqual(3, nested[2]); + Assert.AreEqual(4, nested[3]); + Assert.AreEqual(5, nested[4]); + } +#endif + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs index 5365e848..4511da6c 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; +using Newtonsoft.Json.Linq; #if !(NET20 || NET35 || PORTABLE || PORTABLE40) using System.Numerics; #endif @@ -270,15 +271,15 @@ Newtonsoft.Json Error: 0 : Error! Assert.AreEqual("Started deserializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path 'IntList', line 2, position 13.", traceWriter.TraceRecords[0].Message); Assert.AreEqual("Started deserializing System.Collections.Generic.IList`1[System.Int32]. Path 'IntList', line 2, position 15.", traceWriter.TraceRecords[1].Message); - Assert.AreEqual("Finished deserializing System.Collections.Generic.IList`1[System.Int32]. Path 'IntList', line 5, position 4.", traceWriter.TraceRecords[2].Message); + Assert.IsTrue(traceWriter.TraceRecords[2].Message.StartsWith("Finished deserializing System.Collections.Generic.IList`1[System.Int32]. Path 'IntList'")); Assert.AreEqual("Started deserializing System.String[]. Path 'StringArray', line 6, position 19.", traceWriter.TraceRecords[3].Message); - Assert.AreEqual("Finished deserializing System.String[]. Path 'StringArray', line 9, position 4.", traceWriter.TraceRecords[4].Message); + Assert.IsTrue(traceWriter.TraceRecords[4].Message.StartsWith("Finished deserializing System.String[]. Path 'StringArray'")); Assert.AreEqual("Deserializing System.Version using a non-default constructor 'Void .ctor(Int32, Int32, Int32, Int32)'. Path 'Version.Major', line 11, position 13.", traceWriter.TraceRecords[5].Message); - Assert.AreEqual("Started deserializing System.Version. Path 'Version', line 17, position 4.", traceWriter.TraceRecords[6].Message); - Assert.AreEqual("Finished deserializing System.Version. Path 'Version', line 17, position 4.", traceWriter.TraceRecords[7].Message); + Assert.IsTrue(traceWriter.TraceRecords[6].Message.StartsWith("Started deserializing System.Version. Path 'Version'")); + Assert.IsTrue(traceWriter.TraceRecords[7].Message.StartsWith("Finished deserializing System.Version. Path 'Version'")); Assert.AreEqual("Started deserializing System.Collections.Generic.IDictionary`2[System.String,System.String]. Path 'StringDictionary.1', line 19, position 9.", traceWriter.TraceRecords[8].Message); - Assert.AreEqual("Finished deserializing System.Collections.Generic.IDictionary`2[System.String,System.String]. Path 'StringDictionary', line 22, position 4.", traceWriter.TraceRecords[9].Message); - Assert.AreEqual("Finished deserializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path '', line 23, position 2.", traceWriter.TraceRecords[10].Message); + Assert.IsTrue(traceWriter.TraceRecords[9].Message.StartsWith("Finished deserializing System.Collections.Generic.IDictionary`2[System.String,System.String]. Path 'StringDictionary'")); + Assert.IsTrue(traceWriter.TraceRecords[10].Message.StartsWith("Finished deserializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path ''")); Assert.IsFalse(traceWriter.TraceRecords.Any(r => r.Level == TraceLevel.Verbose)); } @@ -402,7 +403,7 @@ Newtonsoft.Json Error: 0 : Error! Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Read object reference Id '1' for Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'other', line 3, position 11.")); Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Read object reference Id '2' for Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'other.blah', line 5, position 12.")); - Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Resolved object reference '1' to Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'self', line 9, position 4.")); + Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message.StartsWith("Resolved object reference '1' to Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'self'"))); } [Test] @@ -523,12 +524,12 @@ Newtonsoft.Json Error: 0 : Error! Assert.AreEqual("Started deserializing System.Collections.Generic.List`1[System.Object]. Path '$values', line 3, position 15.", traceWriter.TraceRecords[1].Message); Assert.AreEqual("Resolved type 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib' to System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0].$type', line 5, position 120.", traceWriter.TraceRecords[2].Message); Assert.AreEqual("Started deserializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0].key!', line 6, position 14.", traceWriter.TraceRecords[3].Message); - Assert.AreEqual("Finished deserializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0]', line 7, position 6.", traceWriter.TraceRecords[4].Message); + Assert.IsTrue(traceWriter.TraceRecords[4].Message.StartsWith("Finished deserializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0]'")); Assert.AreEqual("Resolved type 'System.Version, mscorlib' to System.Version. Path '$values[1].$type', line 9, position 42.", traceWriter.TraceRecords[5].Message); Assert.AreEqual("Deserializing System.Version using a non-default constructor 'Void .ctor(Int32, Int32, Int32, Int32)'. Path '$values[1].Major', line 10, position 15.", traceWriter.TraceRecords[6].Message); - Assert.AreEqual("Started deserializing System.Version. Path '$values[1]', line 16, position 6.", traceWriter.TraceRecords[7].Message); - Assert.AreEqual("Finished deserializing System.Version. Path '$values[1]', line 16, position 6.", traceWriter.TraceRecords[8].Message); - Assert.AreEqual("Finished deserializing System.Collections.Generic.List`1[System.Object]. Path '$values', line 17, position 4.", traceWriter.TraceRecords[9].Message); + Assert.IsTrue(traceWriter.TraceRecords[7].Message.StartsWith("Started deserializing System.Version. Path '$values[1]'")); + Assert.IsTrue(traceWriter.TraceRecords[8].Message.StartsWith("Finished deserializing System.Version. Path '$values[1]'")); + Assert.IsTrue(traceWriter.TraceRecords[9].Message.StartsWith("Finished deserializing System.Collections.Generic.List`1[System.Object]. Path '$values'")); } #if !(NETFX_CORE || PORTABLE || PORTABLE40) @@ -552,7 +553,7 @@ Newtonsoft.Json Error: 0 : Error! }); }); - Assert.AreEqual("Deserializing System.Exception using ISerializable constructor. Path '', line 1, position 2.", traceWriter.TraceRecords[0].Message); + Assert.IsTrue(traceWriter.TraceRecords[0].Message.StartsWith("Deserializing System.Exception using ISerializable constructor. Path ''")); Assert.AreEqual(TraceLevel.Info, traceWriter.TraceRecords[0].Level); Assert.AreEqual("Error deserializing System.Exception. Member 'ClassName' was not found. Path '', line 1, position 2.", traceWriter.TraceRecords[1].Message); Assert.AreEqual(TraceLevel.Error, traceWriter.TraceRecords[1].Level); @@ -576,7 +577,7 @@ Newtonsoft.Json Error: 0 : Error! Assert.AreEqual("Started deserializing Newtonsoft.Json.Tests.TestObjects.Person. Path 'MissingMemberProperty', line 1, position 25.", traceWriter.TraceRecords[0].Message); Assert.AreEqual("Could not find member 'MissingMemberProperty' on Newtonsoft.Json.Tests.TestObjects.Person. Path 'MissingMemberProperty', line 1, position 25.", traceWriter.TraceRecords[1].Message); - Assert.AreEqual("Finished deserializing Newtonsoft.Json.Tests.TestObjects.Person. Path '', line 1, position 30.", traceWriter.TraceRecords[2].Message); + Assert.IsTrue(traceWriter.TraceRecords[2].Message.StartsWith("Finished deserializing Newtonsoft.Json.Tests.TestObjects.Person. Path ''")); } [Test] @@ -604,8 +605,8 @@ Newtonsoft.Json Error: 0 : Error! Assert.AreEqual("Deserializing System.Version using a non-default constructor 'Void .ctor(Int32, Int32, Int32, Int32)'. Path 'Major', line 2, position 11.", traceWriter.TraceRecords[0].Message); Assert.AreEqual("Could not find member 'MissingMemberProperty' on System.Version. Path 'MissingMemberProperty', line 8, position 32.", traceWriter.TraceRecords[1].Message); - Assert.AreEqual("Started deserializing System.Version. Path '', line 9, position 2.", traceWriter.TraceRecords[2].Message); - Assert.AreEqual("Finished deserializing System.Version. Path '', line 9, position 2.", traceWriter.TraceRecords[3].Message); + Assert.IsTrue(traceWriter.TraceRecords[2].Message.StartsWith("Started deserializing System.Version. Path ''")); + Assert.IsTrue(traceWriter.TraceRecords[3].Message.StartsWith("Finished deserializing System.Version. Path ''")); } [Test] @@ -673,7 +674,7 @@ Newtonsoft.Json Error: 0 : Error! SpecifiedTestClass deserialized = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { TraceWriter = traceWriter }); Assert.AreEqual("Started deserializing Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass. Path 'Age', line 2, position 9.", traceWriter.TraceRecords[0].Message); - Assert.AreEqual("Finished deserializing Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass. Path '', line 3, position 2.", traceWriter.TraceRecords[1].Message); + Assert.IsTrue(traceWriter.TraceRecords[1].Message.StartsWith("Finished deserializing Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass. Path ''")); Assert.IsNull(deserialized.Name); Assert.IsFalse(deserialized.NameSpecified); @@ -707,7 +708,7 @@ Newtonsoft.Json Error: 0 : Error! Assert.AreEqual("IsSpecified for property 'Name' on Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass set to true. Path 'Name', line 2, position 18.", traceWriter.TraceRecords[1].Message); Assert.AreEqual("IsSpecified for property 'Weight' on Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass set to true. Path 'Weight', line 4, position 14.", traceWriter.TraceRecords[2].Message); Assert.AreEqual("IsSpecified for property 'Height' on Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass set to true. Path 'Height', line 5, position 14.", traceWriter.TraceRecords[3].Message); - Assert.AreEqual("Finished deserializing Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass. Path '', line 7, position 2.", traceWriter.TraceRecords[4].Message); + Assert.IsTrue(traceWriter.TraceRecords[4].Message.StartsWith("Finished deserializing Newtonsoft.Json.Tests.Serialization.SpecifiedTestClass. Path ''")); Assert.AreEqual("James", deserialized.Name); Assert.IsTrue(deserialized.NameSpecified); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index 5684dbbc..cb155fb0 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -1591,6 +1591,8 @@ namespace Newtonsoft.Json.Tests.Serialization }; string serializedString = JsonConvert.SerializeObject(inputContext, jsonSerializerSettings); + Console.WriteLine(serializedString); + var deserializedObject = (Dictionary)JsonConvert.DeserializeObject(serializedString, jsonSerializerSettings); Assert.AreEqual(someValue, deserializedObject[contextKey]); diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 22ac7c9f..5ece9382 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -420,7 +420,7 @@ namespace Newtonsoft.Json if (t == JsonToken.Date) { if (Value is DateTime) - SetToken(JsonToken.Date, new DateTimeOffset((DateTime)Value)); + SetToken(JsonToken.Date, new DateTimeOffset((DateTime)Value), false); return (DateTimeOffset)Value; } @@ -440,7 +440,7 @@ namespace Newtonsoft.Json if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) { - SetToken(JsonToken.Date, dt); + SetToken(JsonToken.Date, dt, false); return dt; } else @@ -479,7 +479,7 @@ namespace Newtonsoft.Json { byte[] data = ReadAsBytes(); ReadInternal(); - SetToken(JsonToken.Bytes, data); + SetToken(JsonToken.Bytes, data, false); return data; } @@ -488,7 +488,7 @@ namespace Newtonsoft.Json { string s = (string)Value; byte[] data = (s.Length == 0) ? new byte[0] : Convert.FromBase64String(s); - SetToken(JsonToken.Bytes, data); + SetToken(JsonToken.Bytes, data, false); return data; } @@ -512,7 +512,7 @@ namespace Newtonsoft.Json break; case JsonToken.EndArray: byte[] d = data.ToArray(); - SetToken(JsonToken.Bytes, d); + SetToken(JsonToken.Bytes, d, false); return d; case JsonToken.Comment: // skip @@ -553,7 +553,7 @@ namespace Newtonsoft.Json if (t == JsonToken.Integer || t == JsonToken.Float) { if (!(Value is decimal)) - SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture)); + SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture), false); return (decimal)Value; } @@ -573,7 +573,7 @@ namespace Newtonsoft.Json if (decimal.TryParse(s, NumberStyles.Number, Culture, out d)) { - SetToken(JsonToken.Float, d); + SetToken(JsonToken.Float, d, false); return d; } else @@ -610,7 +610,7 @@ namespace Newtonsoft.Json if (t == JsonToken.Integer || t == JsonToken.Float) { if (!(Value is int)) - SetToken(JsonToken.Integer, Convert.ToInt32(Value, CultureInfo.InvariantCulture)); + SetToken(JsonToken.Integer, Convert.ToInt32(Value, CultureInfo.InvariantCulture), false); return (int)Value; } @@ -630,7 +630,7 @@ namespace Newtonsoft.Json if (int.TryParse(s, NumberStyles.Integer, Culture, out i)) { - SetToken(JsonToken.Integer, i); + SetToken(JsonToken.Integer, i, false); return i; } else @@ -680,7 +680,7 @@ namespace Newtonsoft.Json else s = Value.ToString(); - SetToken(JsonToken.String, s); + SetToken(JsonToken.String, s, false); return s; } } @@ -723,7 +723,7 @@ namespace Newtonsoft.Json if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) { dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling); - SetToken(JsonToken.Date, dt); + SetToken(JsonToken.Date, dt, false); return dt; } else @@ -790,7 +790,7 @@ namespace Newtonsoft.Json /// The new token. protected void SetToken(JsonToken newToken) { - SetToken(newToken, null); + SetToken(newToken, null, true); } /// @@ -799,6 +799,11 @@ namespace Newtonsoft.Json /// The new token. /// The value. protected void SetToken(JsonToken newToken, object value) + { + SetToken(newToken, value, true); + } + + internal void SetToken(JsonToken newToken, object value, bool updateIndex) { _tokenType = newToken; _value = value; @@ -845,7 +850,8 @@ namespace Newtonsoft.Json else SetFinished(); - UpdateScopeWithFinishedValue(); + if (updateIndex) + UpdateScopeWithFinishedValue(); break; } } diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index bd656d39..b1902096 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -53,6 +53,7 @@ namespace Newtonsoft.Json internal NullValueHandling _nullValueHandling; internal DefaultValueHandling _defaultValueHandling; internal ConstructorHandling _constructorHandling; + internal SpecialPropertyHandling _specialPropertyHandling; internal JsonConverterCollection _converters; internal IContractResolver _contractResolver; internal ITraceWriter _traceWriter; @@ -257,6 +258,22 @@ namespace Newtonsoft.Json } } + /// + /// Gets or sets how special properties are used during deserialization. + /// + /// The special properties handling. + public virtual SpecialPropertyHandling SpecialPropertyHandling + { + get { return _specialPropertyHandling; } + set + { + if (value < SpecialPropertyHandling.Default || value > SpecialPropertyHandling.ReadAhead) + throw new ArgumentOutOfRangeException("value"); + + _specialPropertyHandling = value; + } + } + /// /// Gets a collection that will be used during serialization. /// @@ -426,6 +443,7 @@ namespace Newtonsoft.Json _preserveReferencesHandling = JsonSerializerSettings.DefaultPreserveReferencesHandling; _constructorHandling = JsonSerializerSettings.DefaultConstructorHandling; _typeNameHandling = JsonSerializerSettings.DefaultTypeNameHandling; + _specialPropertyHandling = JsonSerializerSettings.DefaultSpecialPropertyHandling; _context = JsonSerializerSettings.DefaultContext; _binder = DefaultSerializationBinder.Instance; @@ -515,6 +533,8 @@ namespace Newtonsoft.Json // serializer specific if (settings._typeNameHandling != null) serializer.TypeNameHandling = settings.TypeNameHandling; + if (settings._specialPropertyHandling != null) + serializer.SpecialPropertyHandling = settings.SpecialPropertyHandling; if (settings._typeNameAssemblyFormat != null) serializer.TypeNameAssemblyFormat = settings.TypeNameAssemblyFormat; if (settings._preserveReferencesHandling != null) diff --git a/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/Src/Newtonsoft.Json/JsonSerializerSettings.cs index 8cb6f459..40b6df55 100644 --- a/Src/Newtonsoft.Json/JsonSerializerSettings.cs +++ b/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -45,6 +45,7 @@ namespace Newtonsoft.Json internal const PreserveReferencesHandling DefaultPreserveReferencesHandling = PreserveReferencesHandling.None; internal const ConstructorHandling DefaultConstructorHandling = ConstructorHandling.Default; internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None; + internal const SpecialPropertyHandling DefaultSpecialPropertyHandling = SpecialPropertyHandling.Default; internal const FormatterAssemblyStyle DefaultTypeNameAssemblyFormat = FormatterAssemblyStyle.Simple; internal static readonly StreamingContext DefaultContext; @@ -83,6 +84,7 @@ namespace Newtonsoft.Json internal StreamingContext? _context; internal ConstructorHandling? _constructorHandling; internal TypeNameHandling? _typeNameHandling; + internal SpecialPropertyHandling? _specialPropertyHandling; /// /// Gets or sets how reference loops (e.g. a class referencing itself) is handled. @@ -160,6 +162,16 @@ namespace Newtonsoft.Json set { _typeNameHandling = value; } } + /// + /// Gets or sets how special properties are used during deserialization. + /// + /// The special properties handling. + public SpecialPropertyHandling SpecialPropertyHandling + { + get { return _specialPropertyHandling ?? DefaultSpecialPropertyHandling; } + set { _specialPropertyHandling = value; } + } + /// /// Gets or sets how a type name assembly is written and resolved by the serializer. /// diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index a99cbd42..c161c2d9 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -687,7 +687,7 @@ namespace Newtonsoft.Json.Linq break; case JsonToken.StartConstructor: JConstructor constructor = new JConstructor(r.Value.ToString()); - constructor.SetLineInfo(constructor); + constructor.SetLineInfo(lineInfo); parent.Add(constructor); parent = constructor; break; diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index c8bbf9e9..86d2a8e2 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -190,7 +190,11 @@ namespace Newtonsoft.Json.Linq for (int i = 0; i < ancestors.Count; i++) { JToken current = ancestors[i]; - JToken next = (i + 1 < ancestors.Count) ? ancestors[i + 1] : null; + JToken next = null; + if (i + 1 < ancestors.Count) + next = ancestors[i + 1]; + else if (ancestors[i].Type == JTokenType.Property) + next = ancestors[i]; if (next != null) { @@ -1636,7 +1640,7 @@ namespace Newtonsoft.Json.Linq /// An that can be used to read this token and its descendants. public JsonReader CreateReader() { - return new JTokenReader(this); + return new JTokenReader(this, Path); } internal static JToken FromObjectInternal(object o, JsonSerializer jsonSerializer) diff --git a/Src/Newtonsoft.Json/Linq/JTokenReader.cs b/Src/Newtonsoft.Json/Linq/JTokenReader.cs index dfdbf7dd..eb50635b 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenReader.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenReader.cs @@ -33,9 +33,10 @@ namespace Newtonsoft.Json.Linq /// public class JTokenReader : JsonReader, IJsonLineInfo { + private readonly string _initialPath; private readonly JToken _root; private JToken _parent; - private JToken _current; + internal JToken _current; /// /// Initializes a new instance of the class. @@ -49,6 +50,12 @@ namespace Newtonsoft.Json.Linq _current = token; } + internal JTokenReader(JToken token, string initialPath) + : this(token) + { + _initialPath = initialPath; + } + /// /// Reads the next JSON token from the stream as a . /// @@ -227,7 +234,7 @@ namespace Newtonsoft.Json.Linq SetToken(JsonToken.StartArray); break; case JTokenType.Constructor: - SetToken(JsonToken.StartConstructor); + SetToken(JsonToken.StartConstructor, ((JConstructor)token).Name); break; case JTokenType.Property: SetToken(JsonToken.PropertyName, ((JProperty)token).Name); @@ -319,5 +326,30 @@ namespace Newtonsoft.Json.Linq return 0; } } + + /// + /// Gets the path of the current JSON token. + /// + public override string Path + { + get + { + string path = base.Path; + + if (!string.IsNullOrEmpty(_initialPath)) + { + if (string.IsNullOrEmpty(path)) + return _initialPath; + + if (_initialPath.EndsWith("]", StringComparison.OrdinalIgnoreCase) + || path.StartsWith("[", StringComparison.OrdinalIgnoreCase)) + path = _initialPath + path; + else + path = _initialPath + "." + path; + } + + return path; + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 1df70fc0..6c09e7a7 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -126,6 +126,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 0e449f82..2127a17f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -137,6 +137,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj index ed91782f..77e02e7d 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj @@ -137,6 +137,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj index e78b4349..a9839d9e 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj @@ -192,6 +192,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj index d7f53d12..be6fe45f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj @@ -197,6 +197,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 5b9ae869..530e065f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -136,6 +136,7 @@ + diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index c0e3a986..0476245f 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.1.16929")] +[assembly: AssemblyFileVersion("6.0.1.17012")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index a1eed470..5d2cd6e1 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -359,12 +359,31 @@ namespace Newtonsoft.Json.Serialization private object CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue) { - CheckedRead(reader); - string id; object newValue; - if (ReadSpecialProperties(reader, ref objectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) - return newValue; + + if (Serializer.SpecialPropertyHandling == SpecialPropertyHandling.ReadAhead) + { + var tokenReader = reader as JTokenReader; + if (tokenReader == null) + { + JToken t = JToken.ReadFrom(reader); + tokenReader = (JTokenReader)t.CreateReader(); + reader = tokenReader; + + // start + CheckedRead(reader); + } + + if (ReadSpecialPropertiesToken(tokenReader, ref objectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) + return newValue; + } + else + { + CheckedRead(reader); + if (ReadSpecialProperties(reader, ref objectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) + return newValue; + } if (HasNoDefinedType(contract)) return CreateJObject(reader); @@ -473,6 +492,80 @@ To fix this error either change the JSON to a {1} or change the deserialized typ ".FormatWith(CultureInfo.InvariantCulture, objectType, GetExpectedDescription(contract))); } + private bool ReadSpecialPropertiesToken(JTokenReader reader, ref Type objectType, ref JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue, out object newValue, out string id) + { + id = null; + newValue = null; + + if (reader.TokenType == JsonToken.StartObject) + { + JObject current = (JObject)reader._current; + + JToken refToken = current[JsonTypeReflector.RefPropertyName]; + if (refToken != null) + { + if (refToken.Type != JTokenType.String && refToken.Type != JTokenType.Null) + throw JsonSerializationException.Create(refToken, refToken.Path, "JSON reference {0} property must have a string or null value.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName), null); + + JToken property = refToken.Parent; + JToken additionalContent = null; + if (property.Next != null) + additionalContent = property.Next; + else if (property.Previous != null) + additionalContent = property.Previous; + + string reference = (string)refToken; + + refToken.Parent.Remove(); + + if (reference != null) + { + if (additionalContent != null) + throw JsonSerializationException.Create(additionalContent, additionalContent.Path, "Additional content found in JSON reference object. A JSON reference object should only have a {0} property.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName), null); + + newValue = Serializer.GetReferenceResolver().ResolveReference(this, reference); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved object reference '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, reference, newValue.GetType())), null); + + CheckedRead(reader); + return true; + } + } + JToken typeToken = current[JsonTypeReflector.TypePropertyName]; + if (typeToken != null) + { + string qualifiedTypeName = (string)typeToken; + JsonReader typeTokenReader = typeToken.CreateReader(); + CheckedRead(typeTokenReader); + ResolveTypeName(typeTokenReader, ref objectType, ref contract, member, containerContract, containerMember, qualifiedTypeName); + + typeToken.Parent.Remove(); + } + JToken idToken = current[JsonTypeReflector.IdPropertyName]; + if (idToken != null) + { + id = (string)idToken; + + idToken.Parent.Remove(); + } + JToken valuesToken = current[JsonTypeReflector.ArrayValuesPropertyName]; + if (valuesToken != null) + { + JsonReader listReader = valuesToken.CreateReader(); + CheckedRead(listReader); + newValue = CreateList(listReader, objectType, contract, member, existingValue, id); + + valuesToken.Parent.Remove(); + CheckedRead(reader); + return true; + } + } + + CheckedRead(reader); + return false; + } + private bool ReadSpecialProperties(JsonReader reader, ref Type objectType, ref JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue, out object newValue, out string id) { id = null; @@ -524,44 +617,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ CheckedRead(reader); string qualifiedTypeName = reader.Value.ToString(); - TypeNameHandling resolvedTypeNameHandling = - ((member != null) ? member.TypeNameHandling : null) - ?? ((containerContract != null) ? containerContract.ItemTypeNameHandling : null) - ?? ((containerMember != null) ? containerMember.ItemTypeNameHandling : null) - ?? Serializer._typeNameHandling; - - if (resolvedTypeNameHandling != TypeNameHandling.None) - { - string typeName; - string assemblyName; - ReflectionUtils.SplitFullyQualifiedTypeName(qualifiedTypeName, out typeName, out assemblyName); - - Type specifiedType; - try - { - specifiedType = Serializer._binder.BindToType(assemblyName, typeName); - } - catch (Exception ex) - { - throw JsonSerializationException.Create(reader, "Error resolving type specified in JSON '{0}'.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName), ex); - } - - if (specifiedType == null) - throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' was not resolved.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName)); - - if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) - TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved type '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName, specifiedType)), null); - - if (objectType != null -#if !(NET35 || NET20 || PORTABLE40) - && objectType != typeof(IDynamicMetaObjectProvider) -#endif - && !objectType.IsAssignableFrom(specifiedType)) - throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' is not compatible with '{1}'.".FormatWith(CultureInfo.InvariantCulture, specifiedType.AssemblyQualifiedName, objectType.AssemblyQualifiedName)); - - objectType = specifiedType; - contract = GetContractSafe(specifiedType); - } + ResolveTypeName(reader, ref objectType, ref contract, member, containerContract, containerMember, qualifiedTypeName); CheckedRead(reader); @@ -595,6 +651,48 @@ To fix this error either change the JSON to a {1} or change the deserialized typ return false; } + private void ResolveTypeName(JsonReader reader, ref Type objectType, ref JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, string qualifiedTypeName) + { + TypeNameHandling resolvedTypeNameHandling = + ((member != null) ? member.TypeNameHandling : null) + ?? ((containerContract != null) ? containerContract.ItemTypeNameHandling : null) + ?? ((containerMember != null) ? containerMember.ItemTypeNameHandling : null) + ?? Serializer._typeNameHandling; + + if (resolvedTypeNameHandling != TypeNameHandling.None) + { + string typeName; + string assemblyName; + ReflectionUtils.SplitFullyQualifiedTypeName(qualifiedTypeName, out typeName, out assemblyName); + + Type specifiedType; + try + { + specifiedType = Serializer._binder.BindToType(assemblyName, typeName); + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error resolving type specified in JSON '{0}'.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName), ex); + } + + if (specifiedType == null) + throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' was not resolved.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName)); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved type '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName, specifiedType)), null); + + if (objectType != null +#if !(NET35 || NET20 || PORTABLE40) + && objectType != typeof(IDynamicMetaObjectProvider) +#endif + && !objectType.IsAssignableFrom(specifiedType)) + throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' is not compatible with '{1}'.".FormatWith(CultureInfo.InvariantCulture, specifiedType.AssemblyQualifiedName, objectType.AssemblyQualifiedName)); + + objectType = specifiedType; + contract = GetContractSafe(specifiedType); + } + } + private JsonArrayContract EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract) { if (contract == null) diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs index f42600af..7016f423 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -108,6 +108,12 @@ namespace Newtonsoft.Json.Serialization set { _serializer.TypeNameHandling = value; } } + public override SpecialPropertyHandling SpecialPropertyHandling + { + get { return _serializer.SpecialPropertyHandling; } + set { _serializer.SpecialPropertyHandling = value; } + } + public override FormatterAssemblyStyle TypeNameAssemblyFormat { get { return _serializer.TypeNameAssemblyFormat; } diff --git a/Src/Newtonsoft.Json/SpecialPropertyHandling.cs b/Src/Newtonsoft.Json/SpecialPropertyHandling.cs new file mode 100644 index 00000000..406ebc1c --- /dev/null +++ b/Src/Newtonsoft.Json/SpecialPropertyHandling.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies special property handling options for the . + /// + public enum SpecialPropertyHandling + { + /// + /// Read special properties located at the start of a JSON object. + /// + Default = 0, + + /// + /// Read special properties located anywhere in a JSON object. Note that this setting will impact performance. + /// + ReadAhead = 1, + } +} \ No newline at end of file From b43afdcc0ac53d617712d666e58192922b8a465d Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 1 Mar 2014 18:31:01 +1300 Subject: [PATCH 2/3] -Added support for MS format JSON dates to ReadAsDateTime -Fixed parsing certain JSON with comments into a JObject -Tests --- .../Linq/JObjectTests.cs | 19 ++ .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 41 +++-- .../PreserveReferencesHandlingTests.cs | 11 +- .../SerializationErrorHandlingTests.cs | 13 +- .../SpecialPropertyHandlingTests.cs | 166 +++++++++++++++++- .../Serialization/TraceWriterTests.cs | 9 +- .../Serialization/TypeNameHandlingTests.cs | 1 + Src/Newtonsoft.Json/JsonReader.cs | 29 ++- Src/Newtonsoft.Json/Linq/JProperty.cs | 4 + .../Properties/AssemblyInfo.cs | 2 +- .../JsonSerializerInternalReader.cs | 88 +++++++--- 12 files changed, 328 insertions(+), 57 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index a8e45072..aeead314 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -57,6 +57,25 @@ namespace Newtonsoft.Json.Tests.Linq [TestFixture] public class JObjectTests : TestFixtureBase { + [Test] + public void JObjectWithComments() + { + string json = @"{ /*comment2*/ + ""Name"": /*comment3*/ ""Apple"" /*comment4*/, /*comment5*/ + ""ExpiryDate"": ""\/Date(1230422400000)\/"", + ""Price"": 3.99, + ""Sizes"": /*comment6*/ [ /*comment7*/ + ""Small"", /*comment8*/ + ""Medium"" /*comment9*/, + /*comment10*/ ""Large"" + /*comment11*/ ] /*comment12*/ + } /*comment13*/"; + + JToken o = JToken.Parse(json); + + Assert.AreEqual("Apple", (string) o["Name"]); + } + [Test] public void WritePropertyWithNoValue() { diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 24837880..380b1a6f 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.1.17012")] +[assembly: AssemblyFileVersion("6.0.1.17101")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index b939a1fb..9d22e2a3 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -2099,15 +2099,13 @@ keyword such as type of business."" }); Assert.AreEqual(@"{""DefaultConverter"":""\/Date(0)\/"",""MemberConverter"":""1970-01-01T00:00:00Z""}", json); - ExceptionAssert.Throws( - "Could not convert string to DateTime: /Date(0)/. Path 'DefaultConverter', line 1, position 33.", - () => - { - JsonConvert.DeserializeObject(json, new JsonSerializerSettings - { - DateParseHandling = DateParseHandling.None - }); - }); + var m2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + DateParseHandling = DateParseHandling.None + }); + + Assert.AreEqual(new DateTime(1970, 1, 1), m2.DefaultConverter); + Assert.AreEqual(new DateTime(1970, 1, 1), m2.MemberConverter); } [Test] @@ -2941,16 +2939,16 @@ keyword such as type of business."" [Test] public void TypedObjectDeserializationWithComments() { - string json = @"/*comment*/ { /*comment*/ - ""Name"": /*comment*/ ""Apple"" /*comment*/, /*comment*/ + string json = @"/*comment1*/ { /*comment2*/ + ""Name"": /*comment3*/ ""Apple"" /*comment4*/, /*comment5*/ ""ExpiryDate"": ""\/Date(1230422400000)\/"", ""Price"": 3.99, - ""Sizes"": /*comment*/ [ /*comment*/ - ""Small"", /*comment*/ - ""Medium"" /*comment*/, - /*comment*/ ""Large"" - /*comment*/ ] /*comment*/ - } /*comment*/"; + ""Sizes"": /*comment6*/ [ /*comment7*/ + ""Small"", /*comment8*/ + ""Medium"" /*comment9*/, + /*comment10*/ ""Large"" + /*comment11*/ ] /*comment12*/ + } /*comment13*/"; Product deserializedProduct = (Product)JsonConvert.DeserializeObject(json, typeof(Product)); @@ -3881,7 +3879,11 @@ Path ''")); string json = "{'$id':'1',key1:'value1',key2:'value2',key3:'value3'}"; ExceptionAssert.Throws("Cannot preserve reference to readonly dictionary, or dictionary created from a non-default constructor: Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+DictionaryWithNoDefaultConstructor. Path 'key1', line 1, position 16.", - () => JsonConvert.DeserializeObject(json, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All })); + () => JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + SpecialPropertyHandling = SpecialPropertyHandling.Default + })); } public class DictionaryWithNoDefaultConstructor : Dictionary @@ -5505,6 +5507,7 @@ To fix this error either change the environment to be fully trusted, change the JsonTextReader reader = new JsonTextReader(new StringReader(serializeObject)); JsonSerializer serializer = new JsonSerializer(); + serializer.DateParseHandling = DateParseHandling.None; var deserializeObject = serializer.Deserialize(reader); @@ -5847,7 +5850,7 @@ To fix this error either change the environment to be fully trusted, change the { var data = new DecimalTest(decimal.MinValue); var json = JsonConvert.SerializeObject(data); - var obj = JsonConvert.DeserializeObject(json); + var obj = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { SpecialPropertyHandling = SpecialPropertyHandling.Default }); Assert.AreEqual(decimal.MinValue, obj.Value); } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs index d618d945..456e4a5f 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs @@ -295,7 +295,11 @@ namespace Newtonsoft.Json.Tests.Serialization () => { JsonConvert.DeserializeObject(json, - new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); + new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + SpecialPropertyHandling = SpecialPropertyHandling.Default + }); }); } @@ -1063,7 +1067,10 @@ namespace Newtonsoft.Json.Tests.Serialization ExceptionAssert.Throws( "Error reading object reference '1'. Path 'Data.Prop2.MyProperty', line 9, position 20.", - () => JsonConvert.DeserializeObject(json)); + () => JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + SpecialPropertyHandling = SpecialPropertyHandling.Default + })); } } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index fc607370..32518990 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -407,6 +407,7 @@ namespace Newtonsoft.Json.Tests.Serialization string json = "{}"; List errors = new List(); JsonSerializer serializer = new JsonSerializer(); + serializer.SpecialPropertyHandling = SpecialPropertyHandling.Default; serializer.Error += delegate(object sender, ErrorEventArgs args) { errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message); @@ -539,6 +540,7 @@ namespace Newtonsoft.Json.Tests.Serialization "{'badarray':[0,x,2],'goodarray':[0,1,2]}", new JsonSerializerSettings { + SpecialPropertyHandling = SpecialPropertyHandling.Default, Error = (sender, arg) => { errors.Add(arg.ErrorContext.Error.Message); @@ -565,7 +567,11 @@ namespace Newtonsoft.Json.Tests.Serialization const int maxDepth = 256; using (var jsonTextReader = new JsonTextReader(new StringReader(input)) { MaxDepth = maxDepth }) { - JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings { MaxDepth = maxDepth }); + JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings + { + MaxDepth = maxDepth, + SpecialPropertyHandling = SpecialPropertyHandling.Default + }); jsonSerializer.Error += (sender, e) => { errors.Add(e.ErrorContext.Error.Message); @@ -595,7 +601,7 @@ namespace Newtonsoft.Json.Tests.Serialization const int maxDepth = 256; using (var jsonTextReader = new JsonTextReader(new StringReader(input)) { MaxDepth = maxDepth }) { - JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings { MaxDepth = maxDepth }); + JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings { MaxDepth = maxDepth, SpecialPropertyHandling = SpecialPropertyHandling.Default }); jsonSerializer.Error += (sender, e) => { errors.Add(e.ErrorContext.Error.Message); @@ -634,7 +640,8 @@ namespace Newtonsoft.Json.Tests.Serialization { errors.Add(e.ErrorContext.Error.Message); e.ErrorContext.Handled = true; - } + }, + SpecialPropertyHandling = SpecialPropertyHandling.Default }); Assert.AreEqual(true, newDynamicObject.Explicit); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs index a092a576..da3cc244 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SpecialPropertyHandlingTests.cs @@ -54,6 +54,136 @@ namespace Newtonsoft.Json.Tests.Serialization }); } +#if !NETFX_CORE + [Test] + public void SerializeDeserialize_DictionaryContextContainsGuid_DeserializesItemAsGuid() + { + const string contextKey = "k1"; + var someValue = Guid.NewGuid(); + + Dictionary inputContext = new Dictionary(); + inputContext.Add(contextKey, someValue); + + JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings() + { + Formatting = Formatting.Indented, + TypeNameHandling = TypeNameHandling.All, + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }; + string serializedString = JsonConvert.SerializeObject(inputContext, jsonSerializerSettings); + + Console.WriteLine(serializedString); + + var deserializedObject = (Dictionary)JsonConvert.DeserializeObject(serializedString, jsonSerializerSettings); + + Assert.AreEqual(someValue, deserializedObject[contextKey]); + } +#endif + + [Test] + public void DeserializeGuid() + { + Item expected = new Item() + { + SourceTypeID = new Guid("d8220a4b-75b1-4b7a-8112-b7bdae956a45"), + BrokerID = new Guid("951663c4-924e-4c86-a57a-7ed737501dbd"), + Latitude = 33.657145, + Longitude = -117.766684, + TimeStamp = new DateTime(2000, 3, 1, 23, 59, 59, DateTimeKind.Utc), + Payload = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + + string jsonString = JsonConvert.SerializeObject(expected, Formatting.Indented); + + Assert.AreEqual(@"{ + ""SourceTypeID"": ""d8220a4b-75b1-4b7a-8112-b7bdae956a45"", + ""BrokerID"": ""951663c4-924e-4c86-a57a-7ed737501dbd"", + ""Latitude"": 33.657145, + ""Longitude"": -117.766684, + ""TimeStamp"": ""2000-03-01T23:59:59Z"", + ""Payload"": { + ""$type"": ""System.Byte[], mscorlib"", + ""$value"": ""AAECAwQFBgcICQ=="" + } +}", jsonString); + + Item actual = JsonConvert.DeserializeObject(jsonString, new JsonSerializerSettings + { + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + + Assert.AreEqual(new Guid("d8220a4b-75b1-4b7a-8112-b7bdae956a45"), actual.SourceTypeID); + Assert.AreEqual(new Guid("951663c4-924e-4c86-a57a-7ed737501dbd"), actual.BrokerID); + byte[] bytes = (byte[])actual.Payload; + CollectionAssert.AreEquivalent(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, bytes); + } + + [Test] + public void DeserializeListsWithPreserveObjectReferences() + { + string json = @"{ + ""$id"": ""1"", + ""$values"": [ + null, + { + ""$id"": ""2"", + ""$values"": [ + null + ] + }, + { + ""$id"": ""3"", + ""$values"": [ + { + ""$id"": ""4"", + ""$values"": [ + { + ""$ref"": ""1"" + } + ] + } + ] + } + ] +}"; + + PreserveReferencesHandlingTests.CircularList circularList = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + + Assert.AreEqual(3, circularList.Count); + Assert.AreEqual(null, circularList[0]); + Assert.AreEqual(1, circularList[1].Count); + Assert.AreEqual(1, circularList[2].Count); + Assert.AreEqual(1, circularList[2][0].Count); + Assert.IsTrue(ReferenceEquals(circularList, circularList[2][0][0])); + } + + [Test] + public void DeserializeTypeNameOnly() + { + string json = @"{ + ""$id"": ""1"", + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.Employee"", + ""Name"": ""Name!"", + ""Manager"": null +}"; + + ExceptionAssert.Throws( + "Type specified in JSON 'Newtonsoft.Json.Tests.TestObjects.Employee' was not resolved. Path '$type', line 3, position 56.", + () => + { + JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + }); + }); + } + [Test] public void SerializeRefNull() { @@ -67,7 +197,7 @@ namespace Newtonsoft.Json.Tests.Serialization child.Add("Name", "Isabell"); child.Add("Father", reference); - string json = JsonConvert.SerializeObject(child); + string json = JsonConvert.SerializeObject(child, Formatting.Indented); Console.WriteLine(json); @@ -110,6 +240,40 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(employees[0], employees[1].Manager); } + [Test] + public void DeserializeFromJToken() + { + string json = @"[ + { + ""Name"": ""Mike Manager"", + ""$id"": ""1"", + ""Manager"": null + }, + { + ""Name"": ""Joe User"", + ""$id"": ""2"", + ""Manager"": { + ""$ref"": ""1"" + } + } +]"; + + JToken t1 = JToken.Parse(json); + JToken t2 = t1.CloneToken(); + + List employees = t1.ToObject>(JsonSerializer.Create(new JsonSerializerSettings + { + SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead + })); + + Assert.AreEqual(2, employees.Count); + Assert.AreEqual("Mike Manager", employees[0].Name); + Assert.AreEqual("Joe User", employees[1].Name); + Assert.AreEqual(employees[0], employees[1].Manager); + + Assert.IsTrue(JToken.DeepEquals(t1, t2)); + } + #if !(PORTABLE || PORTABLE40) [Test] public void DeserializeGenericObjectListWithTypeName() diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs index 4511da6c..eea5d8f5 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs @@ -136,7 +136,12 @@ Newtonsoft.Json Error: 0 : Error! JsonConvert.DeserializeObject( json, - new JsonSerializerSettings { TraceWriter = traceWriter, Converters = { new JavaScriptDateTimeConverter() } }); + new JsonSerializerSettings + { + TraceWriter = traceWriter, + Converters = { new JavaScriptDateTimeConverter() }, + SpecialPropertyHandling = SpecialPropertyHandling.Default + }); Console.WriteLine(traceWriter); // 2012-11-11T12:08:42.761 Info Started serializing Newtonsoft.Json.Tests.Serialization.Staff. Path ''. @@ -398,6 +403,7 @@ Newtonsoft.Json Error: 0 : Error! new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All, + SpecialPropertyHandling = SpecialPropertyHandling.Default, TraceWriter = traceWriter }); @@ -517,6 +523,7 @@ Newtonsoft.Json Error: 0 : Error! JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, + SpecialPropertyHandling = SpecialPropertyHandling.Default, TraceWriter = traceWriter }); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index cb155fb0..eef960d9 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -122,6 +122,7 @@ namespace Newtonsoft.Json.Tests.Serialization { var reader = new JsonTextReader(new StringReader(sb.ToString())); var ser = new JsonSerializer(); + ser.SpecialPropertyHandling = SpecialPropertyHandling.Default; ser.Deserialize(reader); }); } diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 5ece9382..7a8db4ca 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -438,15 +438,21 @@ namespace Newtonsoft.Json return null; } + object temp; + if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTimeOffset, DateTimeZoneHandling, out temp)) + { + dt = (DateTimeOffset)temp; + SetToken(JsonToken.Date, dt, false); + return dt; + } + if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) { SetToken(JsonToken.Date, dt, false); return dt; } - else - { - throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); - } + + throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); } if (t == JsonToken.EndArray) @@ -720,16 +726,23 @@ namespace Newtonsoft.Json return null; } + object temp; + if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTime, DateTimeZoneHandling, out temp)) + { + dt = (DateTime)temp; + dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling); + SetToken(JsonToken.Date, dt, false); + return dt; + } + if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) { dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling); SetToken(JsonToken.Date, dt, false); return dt; } - else - { - throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); - } + + throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); } if (TokenType == JsonToken.EndArray) diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index 9d77ded3..e13f4917 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -130,6 +130,10 @@ namespace Newtonsoft.Json.Linq internal override void InsertItem(int index, JToken item, bool skipParentCheck) { + // don't add comments to JProperty + if (item != null && item.Type == JTokenType.Comment) + return; + if (Value != null) throw new JsonException("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 0476245f..f728fa4b 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.1.17012")] +[assembly: AssemblyFileVersion("6.0.1.17101")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 5d2cd6e1..d791b156 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -227,20 +227,33 @@ namespace Newtonsoft.Json.Serialization ValidationUtils.ArgumentNotNull(reader, "reader"); // this is needed because we've already read inside the object, looking for special properties - JToken token; using (JTokenWriter writer = new JTokenWriter()) { writer.WriteStartObject(); - if (reader.TokenType == JsonToken.PropertyName) - writer.WriteToken(reader, reader.Depth - 1, true, true); - else - writer.WriteEndObject(); + do + { + if (reader.TokenType == JsonToken.PropertyName) + { + string propertyName = (string)reader.Value; + if (!reader.Read()) + break; - token = writer.Token; + if (CheckPropertyName(reader, propertyName)) + continue; + + writer.WritePropertyName(propertyName); + writer.WriteToken(reader, true, true); + } + else + { + writer.WriteEndObject(); + return writer.Token; + } + } while (reader.Read()); + + throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); } - - return token; } private object CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue) @@ -252,8 +265,8 @@ namespace Newtonsoft.Json.Serialization { switch (reader.TokenType) { - // populate a typed object or generic dictionary/array - // depending upon whether an objectType was supplied + // populate a typed object or generic dictionary/array + // depending upon whether an objectType was supplied case JsonToken.StartObject: return CreateObject(reader, objectType, contract, member, containerContract, containerMember, existingValue); case JsonToken.StartArray: @@ -369,10 +382,11 @@ namespace Newtonsoft.Json.Serialization { JToken t = JToken.ReadFrom(reader); tokenReader = (JTokenReader)t.CreateReader(); - reader = tokenReader; // start - CheckedRead(reader); + CheckedRead(tokenReader); + + reader = tokenReader; } if (ReadSpecialPropertiesToken(tokenReader, ref objectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) @@ -516,8 +530,6 @@ To fix this error either change the JSON to a {1} or change the deserialized typ string reference = (string)refToken; - refToken.Parent.Remove(); - if (reference != null) { if (additionalContent != null) @@ -528,7 +540,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved object reference '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, reference, newValue.GetType())), null); - CheckedRead(reader); + reader.Skip(); return true; } } @@ -540,14 +552,27 @@ To fix this error either change the JSON to a {1} or change the deserialized typ CheckedRead(typeTokenReader); ResolveTypeName(typeTokenReader, ref objectType, ref contract, member, containerContract, containerMember, qualifiedTypeName); - typeToken.Parent.Remove(); + JToken valueToken = current[JsonTypeReflector.ValuePropertyName]; + if (valueToken != null) + { + while (true) + { + CheckedRead(reader); + if (reader.TokenType == JsonToken.PropertyName) + { + if ((string)reader.Value == JsonTypeReflector.ValuePropertyName) + return false; + } + + CheckedRead(reader); + reader.Skip(); + } + } } JToken idToken = current[JsonTypeReflector.IdPropertyName]; if (idToken != null) { id = (string)idToken; - - idToken.Parent.Remove(); } JToken valuesToken = current[JsonTypeReflector.ArrayValuesPropertyName]; if (valuesToken != null) @@ -556,8 +581,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ CheckedRead(listReader); newValue = CreateList(listReader, objectType, contract, member, existingValue, id); - valuesToken.Parent.Remove(); - CheckedRead(reader); + reader.Skip(); return true; } } @@ -1095,6 +1119,8 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { case JsonToken.PropertyName: object keyValue = reader.Value; + if (CheckPropertyName(reader, keyValue.ToString())) + continue; try { try @@ -1849,6 +1875,9 @@ To fix this error either change the environment to be fully trusted, change the { string memberName = reader.Value.ToString(); + if (CheckPropertyName(reader, memberName)) + continue; + try { // attempt exact case match first @@ -1891,8 +1920,8 @@ To fix this error either change the environment to be fully trusted, change the else throw; } - } break; + } case JsonToken.EndObject: finished = true; break; @@ -1913,6 +1942,23 @@ To fix this error either change the environment to be fully trusted, change the return newObject; } + private bool CheckPropertyName(JsonReader reader, string memberName) + { + if (Serializer.SpecialPropertyHandling == SpecialPropertyHandling.ReadAhead) + { + switch (memberName) + { + case "$type": + case "$id": + case "$ref": + case "$values": + reader.Skip(); + return true; + } + } + return false; + } + private void SetExtensionData(JsonObjectContract contract, JsonProperty member, JsonReader reader, string memberName, object o) { if (contract.ExtensionDataSetter != null) From f5684137177d088e9c41250e35788224270753a9 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 1 Mar 2014 18:39:21 +1300 Subject: [PATCH 3/3] -Clean up --- Src/Newtonsoft.Json/Bson/BsonReader.cs | 5 +++-- Src/Newtonsoft.Json/JsonReader.cs | 5 +++-- Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs | 3 ++- Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs | 2 -- Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs | 3 ++- .../Serialization/JsonSerializerInternalReader.cs | 8 ++++---- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index 2f9923e9..2a68d583 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text; using System.IO; +using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; using Newtonsoft.Json.Linq; @@ -356,7 +357,7 @@ namespace Newtonsoft.Json.Bson { case State.ObjectStart: { - SetToken(JsonToken.PropertyName, "$ref"); + SetToken(JsonToken.PropertyName, JsonTypeReflector.RefPropertyName); _bsonReaderState = BsonReaderState.ReferenceRef; return true; } @@ -381,7 +382,7 @@ namespace Newtonsoft.Json.Bson { if (_bsonReaderState == BsonReaderState.ReferenceRef) { - SetToken(JsonToken.PropertyName, "$id"); + SetToken(JsonToken.PropertyName, JsonTypeReflector.IdPropertyName); _bsonReaderState = BsonReaderState.ReferenceId; return true; } diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 7a8db4ca..20a63879 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Globalization; +using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; #if NET20 using Newtonsoft.Json.Utilities.LinqBridge; @@ -760,13 +761,13 @@ namespace Newtonsoft.Json if (!ReadInternal()) throw JsonReaderException.Create(this, "Unexpected end when reading bytes."); - if (Value.ToString() == "$type") + if (Value.ToString() == JsonTypeReflector.TypePropertyName) { ReadInternal(); if (Value != null && Value.ToString().StartsWith("System.Byte[]")) { ReadInternal(); - if (Value.ToString() == "$value") + if (Value.ToString() == JsonTypeReflector.ValuePropertyName) { return true; } diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs index 8b150289..1688eb49 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json.Serialization; #if NET20 using Newtonsoft.Json.Utilities.LinqBridge; #else @@ -194,7 +195,7 @@ namespace Newtonsoft.Json.Schema throw JsonException.Create(token, token.Path, "Expected object while parsing schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); JToken referenceToken; - if (schemaObject.TryGetValue(JsonSchemaConstants.ReferencePropertyName, out referenceToken)) + if (schemaObject.TryGetValue(JsonTypeReflector.RefPropertyName, out referenceToken)) { JsonSchema deferredSchema = new JsonSchema(); deferredSchema.DeferredReference = (string)referenceToken; diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs index 692517ad..26b824dd 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs @@ -63,8 +63,6 @@ namespace Newtonsoft.Json.Schema public const string OptionValuePropertyName = "value"; public const string OptionLabelPropertyName = "label"; - public const string ReferencePropertyName = "$ref"; - public static readonly IDictionary JsonSchemaTypeMapping = new Dictionary { { "string", JsonSchemaType.String }, diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs index 2ca437cc..e5a817ba 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs @@ -25,6 +25,7 @@ using System.Collections.Generic; using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; #if NET20 using Newtonsoft.Json.Utilities.LinqBridge; @@ -52,7 +53,7 @@ namespace Newtonsoft.Json.Schema if (schema.Id != null && _resolver.GetSchema(schema.Id) != null) { _writer.WriteStartObject(); - _writer.WritePropertyName(JsonSchemaConstants.ReferencePropertyName); + _writer.WritePropertyName(JsonTypeReflector.RefPropertyName); _writer.WriteValue(schema.Id); _writer.WriteEndObject(); } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index d791b156..d093800d 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1948,10 +1948,10 @@ To fix this error either change the environment to be fully trusted, change the { switch (memberName) { - case "$type": - case "$id": - case "$ref": - case "$values": + case JsonTypeReflector.IdPropertyName: + case JsonTypeReflector.RefPropertyName: + case JsonTypeReflector.TypePropertyName: + case JsonTypeReflector.ArrayValuesPropertyName: reader.Skip(); return true; }