#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.Diagnostics; using System.Globalization; #if HAVE_BIG_INTEGER using System.Numerics; #endif using LC.Newtonsoft.Json.Utilities; namespace LC.Newtonsoft.Json.Linq { /// /// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. /// public partial class JTokenWriter : JsonWriter { private JContainer? _token; 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 => _current; /// /// Gets the token being written. /// /// The token being written. public JToken? Token { get { if (_token != null) { return _token; } return _value; } } /// /// Initializes a new instance of the class writing to the given . /// /// The container being written to. public JTokenWriter(JContainer container) { ValidationUtils.ArgumentNotNull(container, nameof(container)); _token = container; _parent = container; } /// /// Initializes a new instance of the class. /// public JTokenWriter() { } /// /// Flushes whatever is in the buffer to the underlying . /// public override void Flush() { } /// /// Closes this writer. /// If is set to true, the JSON is auto-completed. /// /// /// Setting to true has no additional effect, since the underlying is a type that cannot be closed. /// public override void Close() { base.Close(); } /// /// Writes the beginning of a JSON object. /// public override void WriteStartObject() { base.WriteStartObject(); AddParent(new JObject()); } private void AddParent(JContainer container) { if (_parent == null) { _token = container; } else { _parent.AddAndSkipParentCheck(container); } _parent = container; _current = container; } private void RemoveParent() { _current = _parent; _parent = _parent!.Parent; if (_parent != null && _parent.Type == JTokenType.Property) { _parent = _parent.Parent; } } /// /// Writes the beginning of a JSON array. /// public override void WriteStartArray() { base.WriteStartArray(); AddParent(new JArray()); } /// /// Writes the start of a constructor with the given name. /// /// The name of the constructor. public override void WriteStartConstructor(string name) { base.WriteStartConstructor(name); AddParent(new JConstructor(name)); } /// /// Writes the end. /// /// The token. protected override void WriteEnd(JsonToken token) { RemoveParent(); } /// /// Writes the property name of a name/value pair on a JSON object. /// /// The name of the property. public override void WritePropertyName(string name) { // avoid duplicate property name exception // last property name wins (_parent as JObject)?.Remove(name); AddParent(new JProperty(name)); // don't set state until after in case of an error // incorrect state will cause issues if writer is disposed when closing open properties base.WritePropertyName(name); } private void AddValue(object? value, JsonToken token) { AddValue(new JValue(value), token); } internal void AddValue(JValue? value, JsonToken token) { if (_parent != null) { // TryAdd will return false if an invalid JToken type is added. // For example, a JComment can't be added to a JObject. // If there is an invalid JToken type then skip it. if (_parent.TryAdd(value)) { _current = _parent.Last; if (_parent.Type == JTokenType.Property) { _parent = _parent.Parent; } } } else { _value = value ?? JValue.CreateNull(); _current = _value; } } #region WriteValue methods /// /// Writes a value. /// An error will be raised if the value cannot be written as a single JSON token. /// /// The value to write. public override void WriteValue(object? value) { #if HAVE_BIG_INTEGER if (value is BigInteger) { InternalWriteValue(JsonToken.Integer); AddValue(value, JsonToken.Integer); } else #endif { base.WriteValue(value); } } /// /// Writes a null value. /// public override void WriteNull() { base.WriteNull(); AddValue(null, JsonToken.Null); } /// /// Writes an undefined value. /// public override void WriteUndefined() { base.WriteUndefined(); AddValue(null, JsonToken.Undefined); } /// /// Writes raw JSON. /// /// The raw JSON to write. public override void WriteRaw(string? json) { base.WriteRaw(json); AddValue(new JRaw(json), JsonToken.Raw); } /// /// Writes a comment /*...*/ containing the specified text. /// /// Text to place inside the comment. public override void WriteComment(string? text) { base.WriteComment(text); AddValue(JValue.CreateComment(text), JsonToken.Comment); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(string? value) { base.WriteValue(value); AddValue(value, JsonToken.String); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(int value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. [CLSCompliant(false)] public override void WriteValue(uint value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(long value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. [CLSCompliant(false)] public override void WriteValue(ulong value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(float value) { base.WriteValue(value); AddValue(value, JsonToken.Float); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(double value) { base.WriteValue(value); AddValue(value, JsonToken.Float); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(bool value) { base.WriteValue(value); AddValue(value, JsonToken.Boolean); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(short value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. [CLSCompliant(false)] public override void WriteValue(ushort value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(char value) { base.WriteValue(value); string s; #if HAVE_CHAR_TO_STRING_WITH_CULTURE s = value.ToString(CultureInfo.InvariantCulture); #else s = value.ToString(); #endif AddValue(s, JsonToken.String); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(byte value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. [CLSCompliant(false)] public override void WriteValue(sbyte value) { base.WriteValue(value); AddValue(value, JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(decimal value) { base.WriteValue(value); AddValue(value, JsonToken.Float); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(DateTime value) { base.WriteValue(value); value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling); AddValue(value, JsonToken.Date); } #if HAVE_DATE_TIME_OFFSET /// /// Writes a value. /// /// The value to write. public override void WriteValue(DateTimeOffset value) { base.WriteValue(value); AddValue(value, JsonToken.Date); } #endif /// /// Writes a [] value. /// /// The [] value to write. public override void WriteValue(byte[]? value) { base.WriteValue(value); AddValue(value, JsonToken.Bytes); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(TimeSpan value) { base.WriteValue(value); AddValue(value, JsonToken.String); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(Guid value) { base.WriteValue(value); AddValue(value, JsonToken.String); } /// /// Writes a value. /// /// The value to write. public override void WriteValue(Uri? value) { base.WriteValue(value); AddValue(value, JsonToken.String); } #endregion internal override void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate, bool writeComments) { // cloning the token rather than reading then writing it doesn't lose some type information, e.g. Guid, byte[], etc if (reader is JTokenReader tokenReader && 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); } } } }