From b43afdcc0ac53d617712d666e58192922b8a465d Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 1 Mar 2014 18:31:01 +1300 Subject: [PATCH] -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)