#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);
}
}
}
}