From 86c2f803114631fca7df9e82855728e7e71ad89b Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 29 Apr 2016 16:21:41 +1200 Subject: [PATCH] -Fixed JTokenWriter sometimes losing types when writing JToken --- .../Linq/JTokenWriterTest.cs | 94 +++++++++++++++++++ .../Linq/LinqToJsonTest.cs | 13 +++ .../MetadataPropertyHandlingTests.cs | 10 +- Src/Newtonsoft.Json/JsonWriter.cs | 9 +- Src/Newtonsoft.Json/Linq/JTokenWriter.cs | 46 +++++++++ .../Utilities/ReflectionUtils.cs | 4 +- 6 files changed, 167 insertions(+), 9 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs index 4ca7519c..174ec9c1 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs @@ -249,6 +249,100 @@ namespace Newtonsoft.Json.Tests.Linq ]", writer.Token.ToString()); } + [Test] + public void WriteTokenWithParent() + { + JObject o = new JObject + { + ["prop1"] = new JArray(1), + ["prop2"] = 1 + }; + + JTokenWriter writer = new JTokenWriter(); + + writer.WriteStartArray(); + + writer.WriteToken(o.CreateReader()); + + Assert.AreEqual(WriteState.Array, writer.WriteState); + + writer.WriteEndArray(); + + Console.WriteLine(writer.Token.ToString()); + + StringAssert.AreEqual(@"[ + { + ""prop1"": [ + 1 + ], + ""prop2"": 1 + } +]", writer.Token.ToString()); + } + + [Test] + public void WriteTokenWithPropertyParent() + { + JValue v = new JValue(1); + + JTokenWriter writer = new JTokenWriter(); + + writer.WriteStartObject(); + writer.WritePropertyName("Prop1"); + + writer.WriteToken(v.CreateReader()); + + Assert.AreEqual(WriteState.Object, writer.WriteState); + + writer.WriteEndObject(); + + StringAssert.AreEqual(@"{ + ""Prop1"": 1 +}", writer.Token.ToString()); + } + + [Test] + public void WriteValueTokenWithParent() + { + JValue v = new JValue(1); + + JTokenWriter writer = new JTokenWriter(); + + writer.WriteStartArray(); + + writer.WriteToken(v.CreateReader()); + + Assert.AreEqual(WriteState.Array, writer.WriteState); + + writer.WriteEndArray(); + + StringAssert.AreEqual(@"[ + 1 +]", writer.Token.ToString()); + } + + [Test] + public void WriteEmptyToken() + { + JObject o = new JObject(); + JsonReader reader = o.CreateReader(); + while (reader.Read()) + { + } + + JTokenWriter writer = new JTokenWriter(); + + writer.WriteStartArray(); + + writer.WriteToken(reader); + + Assert.AreEqual(WriteState.Array, writer.WriteState); + + writer.WriteEndArray(); + + StringAssert.AreEqual(@"[]", writer.Token.ToString()); + } + [Test] public void WriteRawValue() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs index a9b7f569..346404e4 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -55,6 +55,19 @@ namespace Newtonsoft.Json.Tests.Linq [TestFixture] public class LinqToJsonTest : TestFixtureBase { + [Test] + public void ToObject_Guid() + { + JObject anon = new JObject + { + ["id"] = Guid.NewGuid() + }; + Assert.AreEqual(JTokenType.Guid, anon["id"].Type); + + Dictionary dict = anon.ToObject>(); + Assert.AreEqual(JTokenType.Guid, dict["id"].Type); + } + public class TestClass_ULong { public ulong Value { get; set; } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs index 44d195cc..08c3c6b8 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs @@ -558,7 +558,7 @@ namespace Newtonsoft.Json.Tests.Serialization { ItemWithJTokens actual = (ItemWithJTokens)JsonConvert.DeserializeObject(@"{ ""Payload1"": 1, - ""Payload2"": {}, + ""Payload2"": {'prop1':1,'prop2':[2]}, ""Payload3"": [1], ""$type"": ""Newtonsoft.Json.Tests.Serialization.MetadataPropertyHandlingTests+ItemWithJTokens, Newtonsoft.Json.Tests"" }", @@ -568,8 +568,16 @@ namespace Newtonsoft.Json.Tests.Serialization TypeNameHandling = TypeNameHandling.All }); + Assert.AreEqual(JTokenType.Integer, actual.Payload1.Type); + Assert.AreEqual(1, (int)actual.Payload1); Assert.AreEqual(null, actual.Payload1.Parent); + + Assert.AreEqual(JTokenType.Object, actual.Payload2.Type); + Assert.AreEqual(1, (int)actual.Payload2["prop1"]); + Assert.AreEqual(2, (int)actual.Payload2["prop2"][0]); Assert.AreEqual(null, actual.Payload2.Parent); + + Assert.AreEqual(1, (int)actual.Payload3[0]); Assert.AreEqual(null, actual.Payload3.Parent); } diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 293b1536..40fa2bce 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -626,7 +626,7 @@ namespace Newtonsoft.Json WriteToken(token, null); } - internal void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate, bool writeComments) + internal virtual void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate, bool writeComments) { int initialDepth; @@ -643,11 +643,6 @@ namespace Newtonsoft.Json initialDepth = reader.Depth; } - WriteToken(reader, initialDepth, writeChildren, writeDateConstructorAsDate, writeComments); - } - - internal void WriteToken(JsonReader reader, int initialDepth, bool writeChildren, bool writeDateConstructorAsDate, bool writeComments) - { do { // write a JValue date when the constructor is for a date @@ -657,7 +652,7 @@ namespace Newtonsoft.Json } else { - if (reader.TokenType != JsonToken.Comment || writeComments) + if (writeComments || reader.TokenType != JsonToken.Comment) { WriteToken(reader.TokenType, reader.Value); } diff --git a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs index 03318509..ee571d60 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs @@ -485,5 +485,51 @@ namespace Newtonsoft.Json.Linq AddValue(value, JsonToken.String); } #endregion + + internal override void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate, bool writeComments) + { + JTokenReader tokenReader = reader as JTokenReader; + if (tokenReader != null && writeChildren && writeDateConstructorAsDate && writeComments) + { + if (tokenReader.TokenType == JsonToken.None) + { + if (!tokenReader.Read()) + { + return; + } + } + + JToken value = tokenReader.CurrentToken.CloneToken(); + + if (_parent != null) + { + _parent.Add(value); + _current = _parent.Last; + + // if the writer was in a property then move out of it and up to its parent object + if (_parent.Type == JTokenType.Property) + { + _parent = _parent.Parent; + InternalWriteValue(JsonToken.Null); + } + } + else + { + _current = value; + + if (_token == null && _value == null) + { + _token = value as JContainer; + _value = value as JValue; + } + } + + tokenReader.Skip(); + } + else + { + base.WriteToken(reader, writeChildren, writeDateConstructorAsDate, writeComments); + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index ba79efca..382d8b02 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -769,9 +769,11 @@ namespace Newtonsoft.Json.Utilities Attribute[] attributes = a.Cast().ToArray(); #if (NET20 || NET35) - // ye olde .NET GetCustomAttributes doesn't respect the inherit argument + // ye olde .NET GetCustomAttributes doesn't respect the inherit argument if (inherit && t.BaseType != null) + { attributes = attributes.Union(GetAttributes(t.BaseType, attributeType, inherit)).ToArray(); + } #endif return attributes;