From 55ccac4d92e82d3044dfb9889e06c0ffbd7ab39b Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 17 Dec 2014 19:38:39 +1300 Subject: [PATCH] -Added CurrentToken to JTokenWriter -Added WriteToken(JsonToken, object) to JsonWriter --- .../JsonTextWriterTest.cs | 43 ++++ .../Linq/JTokenWriterTest.cs | 56 +++++ Src/Newtonsoft.Json/JsonWriter.cs | 212 ++++++++++-------- Src/Newtonsoft.Json/Linq/JTokenWriter.cs | 15 +- 4 files changed, 235 insertions(+), 91 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs index b0335bed..194dd38c 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs @@ -825,6 +825,49 @@ namespace Newtonsoft.Json.Tests ]", sb.ToString()); } + [Test] + public void WriteTokenDirect() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteToken(JsonToken.StartArray); + jsonWriter.WriteToken(JsonToken.Integer, 1); + jsonWriter.WriteToken(JsonToken.StartObject); + jsonWriter.WriteToken(JsonToken.PropertyName, "string"); + jsonWriter.WriteToken(JsonToken.Integer, int.MaxValue); + jsonWriter.WriteToken(JsonToken.EndObject); + jsonWriter.WriteToken(JsonToken.EndArray); + } + + Assert.AreEqual(@"[1,{""string"":2147483647}]", sb.ToString()); + } + + [Test] + public void WriteTokenDirect_BadValue() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteToken(JsonToken.StartArray); + + ExceptionAssert.Throws(() => + { + jsonWriter.WriteToken(JsonToken.Integer, "three"); + }, "Input string was not in a correct format."); + + ExceptionAssert.Throws(() => + { + jsonWriter.WriteToken(JsonToken.Integer); + }, @"Value cannot be null. +Parameter name: value"); + } + } + [Test] public void BadWriteEndArray() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs index 81d06bd4..4e1218b8 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs @@ -140,6 +140,62 @@ namespace Newtonsoft.Json.Tests.Linq } } + [Test] + public void CurrentToken() + { + using (JTokenWriter jsonWriter = new JTokenWriter()) + { + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + Assert.AreEqual(null, jsonWriter.CurrentToken); + + jsonWriter.WriteStartObject(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + Assert.AreEqual(jsonWriter.Token, jsonWriter.CurrentToken); + + JObject o = (JObject)jsonWriter.Token; + + jsonWriter.WritePropertyName("CPU"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + Assert.AreEqual(o.Property("CPU"), jsonWriter.CurrentToken); + + jsonWriter.WriteValue("Intel"); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + Assert.AreEqual(o["CPU"], jsonWriter.CurrentToken); + + jsonWriter.WritePropertyName("Drives"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + Assert.AreEqual(o.Property("Drives"), jsonWriter.CurrentToken); + + jsonWriter.WriteStartArray(); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(o["Drives"], jsonWriter.CurrentToken); + + JArray a = (JArray)jsonWriter.CurrentToken; + + jsonWriter.WriteValue("DVD read/writer"); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(a[a.Count - 1], jsonWriter.CurrentToken); + +#if !(NET20 || NET35 || PORTABLE || ASPNETCORE50 || PORTABLE40) + jsonWriter.WriteValue(new BigInteger(123)); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(a[a.Count - 1], jsonWriter.CurrentToken); +#endif + + jsonWriter.WriteValue(new byte[0]); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + Assert.AreEqual(a[a.Count - 1], jsonWriter.CurrentToken); + + jsonWriter.WriteEnd(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + Assert.AreEqual(a, jsonWriter.CurrentToken); + + jsonWriter.WriteEndObject(); + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + Assert.AreEqual(o, jsonWriter.CurrentToken); + } + } + [Test] public void WriteComment() { diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 03c7b4b6..ac922e9d 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -449,6 +449,28 @@ namespace Newtonsoft.Json WriteToken(reader, writeChildren, true); } + /// + /// Writes the token and its value. + /// + /// The to write. + /// + /// The value to write. + /// A value is only required for tokens that have an associated value, e.g. the property name for . + /// A null value can be passed to the method for token's that don't have a value, e.g. . + public void WriteToken(JsonToken token, object value) + { + WriteTokenInternal(token, value); + } + + /// + /// Writes the token. + /// + /// The to write. + public void WriteToken(JsonToken token) + { + WriteTokenInternal(token, null); + } + internal void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate) { int initialDepth; @@ -467,96 +489,11 @@ namespace Newtonsoft.Json { do { - switch (reader.TokenType) - { - case JsonToken.None: - // read to next - break; - case JsonToken.StartObject: - WriteStartObject(); - break; - case JsonToken.StartArray: - WriteStartArray(); - break; - case JsonToken.StartConstructor: - string constructorName = reader.Value.ToString(); - // write a JValue date when the constructor is for a date - if (writeDateConstructorAsDate && string.Equals(constructorName, "Date", StringComparison.Ordinal)) - WriteConstructorDate(reader); - else - WriteStartConstructor(reader.Value.ToString()); - break; - case JsonToken.PropertyName: - WritePropertyName(reader.Value.ToString()); - break; - case JsonToken.Comment: - WriteComment((reader.Value != null) ? reader.Value.ToString() : null); - break; - case JsonToken.Integer: -#if !(NET20 || NET35 || PORTABLE || PORTABLE40) - if (reader.Value is BigInteger) - { - WriteValue((BigInteger)reader.Value); - } - else -#endif - { - WriteValue(Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture)); - } - break; - case JsonToken.Float: - object value = reader.Value; - - if (value is decimal) - WriteValue((decimal)value); - else if (value is double) - WriteValue((double)value); - else if (value is float) - WriteValue((float)value); - else - WriteValue(Convert.ToDouble(value, CultureInfo.InvariantCulture)); - break; - case JsonToken.String: - WriteValue(reader.Value.ToString()); - break; - case JsonToken.Boolean: - WriteValue(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture)); - break; - case JsonToken.Null: - WriteNull(); - break; - case JsonToken.Undefined: - WriteUndefined(); - break; - case JsonToken.EndObject: - WriteEndObject(); - break; - case JsonToken.EndArray: - WriteEndArray(); - break; - case JsonToken.EndConstructor: - WriteEndConstructor(); - break; - case JsonToken.Date: -#if !NET20 - if (reader.Value is DateTimeOffset) - WriteValue((DateTimeOffset)reader.Value); - else -#endif - WriteValue(Convert.ToDateTime(reader.Value, CultureInfo.InvariantCulture)); - break; - case JsonToken.Raw: - WriteRawValue((reader.Value != null) ? reader.Value.ToString() : null); - break; - case JsonToken.Bytes: - if (reader.Value is Guid) - WriteValue((Guid)reader.Value); - else - WriteValue((byte[])reader.Value); - break; - default: - throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", reader.TokenType, "Unexpected token type."); - } + // write a JValue date when the constructor is for a date + if (writeDateConstructorAsDate && reader.TokenType == JsonToken.StartConstructor && string.Equals(reader.Value.ToString(), "Date", StringComparison.Ordinal)) + WriteConstructorDate(reader); + else + WriteTokenInternal(reader.TokenType, reader.Value); } while ( // stop if we have reached the end of the token being read initialDepth - 1 < reader.Depth - (IsEndToken(reader.TokenType) ? 1 : 0) @@ -564,6 +501,101 @@ namespace Newtonsoft.Json && reader.Read()); } + private void WriteTokenInternal(JsonToken tokenType, object value) + { + switch (tokenType) + { + case JsonToken.None: + // read to next + break; + case JsonToken.StartObject: + WriteStartObject(); + break; + case JsonToken.StartArray: + WriteStartArray(); + break; + case JsonToken.StartConstructor: + ValidationUtils.ArgumentNotNull(value, "value"); + WriteStartConstructor(value.ToString()); + break; + case JsonToken.PropertyName: + ValidationUtils.ArgumentNotNull(value, "value"); + WritePropertyName(value.ToString()); + break; + case JsonToken.Comment: + WriteComment((value != null) ? value.ToString() : null); + break; + case JsonToken.Integer: + ValidationUtils.ArgumentNotNull(value, "value"); +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + if (value is BigInteger) + { + WriteValue((BigInteger)value); + } + else +#endif + { + WriteValue(Convert.ToInt64(value, CultureInfo.InvariantCulture)); + } + break; + case JsonToken.Float: + ValidationUtils.ArgumentNotNull(value, "value"); + if (value is decimal) + WriteValue((decimal)value); + else if (value is double) + WriteValue((double)value); + else if (value is float) + WriteValue((float)value); + else + WriteValue(Convert.ToDouble(value, CultureInfo.InvariantCulture)); + break; + case JsonToken.String: + ValidationUtils.ArgumentNotNull(value, "value"); + WriteValue(value.ToString()); + break; + case JsonToken.Boolean: + ValidationUtils.ArgumentNotNull(value, "value"); + WriteValue(Convert.ToBoolean(value, CultureInfo.InvariantCulture)); + break; + case JsonToken.Null: + WriteNull(); + break; + case JsonToken.Undefined: + WriteUndefined(); + break; + case JsonToken.EndObject: + WriteEndObject(); + break; + case JsonToken.EndArray: + WriteEndArray(); + break; + case JsonToken.EndConstructor: + WriteEndConstructor(); + break; + case JsonToken.Date: + ValidationUtils.ArgumentNotNull(value, "value"); +#if !NET20 + if (value is DateTimeOffset) + WriteValue((DateTimeOffset)value); + else +#endif + WriteValue(Convert.ToDateTime(value, CultureInfo.InvariantCulture)); + break; + case JsonToken.Raw: + WriteRawValue((value != null) ? value.ToString() : null); + break; + case JsonToken.Bytes: + ValidationUtils.ArgumentNotNull(value, "value"); + if (value is Guid) + WriteValue((Guid)value); + else + WriteValue((byte[])value); + break; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", tokenType, "Unexpected token type."); + } + } + private void WriteConstructorDate(JsonReader reader) { if (!reader.Read()) diff --git a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs index 4bce4477..bbe985b8 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs @@ -33,7 +33,7 @@ using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Linq { /// - /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. /// public class JTokenWriter : JsonWriter { @@ -41,6 +41,15 @@ namespace Newtonsoft.Json.Linq private JContainer _parent; // used when writer is writing single value and the value has no containing parent private JValue _value; + private JToken _current; + + /// + /// Gets the at the writer's current position. + /// + public JToken CurrentToken + { + get { return _current; } + } /// /// Gets the token being writen. @@ -109,10 +118,12 @@ namespace Newtonsoft.Json.Linq _parent.AddAndSkipParentCheck(container); _parent = container; + _current = container; } private void RemoveParent() { + _current = _parent; _parent = _parent.Parent; if (_parent != null && _parent.Type == JTokenType.Property) @@ -172,6 +183,7 @@ namespace Newtonsoft.Json.Linq if (_parent != null) { _parent.Add(value); + _current = _parent.Last; if (_parent.Type == JTokenType.Property) _parent = _parent.Parent; @@ -179,6 +191,7 @@ namespace Newtonsoft.Json.Linq else { _value = value ?? JValue.CreateNull(); + _current = _value; } }