Files
TouchSocket/RRQMCore/XREF/Newtonsoft.Json/JsonTextWriter.cs
2022-03-20 10:51:15 +08:00

933 lines
30 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//------------------------------------------------------------------------------
// 此代码版权除特别声明或在RRQMCore.XREF命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://www.yuque.com/eo2w71/rrqm
// 交流QQ群234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
#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 License
using System;
#if HAVE_BIG_INTEGER
using System.Numerics;
#endif
using System.IO;
using RRQMCore.XREF.Newtonsoft.Json.Utilities;
namespace RRQMCore.XREF.Newtonsoft.Json
{
/// <summary>
/// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
/// </summary>
public partial class JsonTextWriter : JsonWriter
{
private const int IndentCharBufferSize = 12;
private readonly TextWriter _writer;
private Base64Encoder _base64Encoder;
private char _indentChar;
private int _indentation;
private char _quoteChar;
private bool _quoteName;
private bool[] _charEscapeFlags;
private char[] _writeBuffer;
private IArrayPool<char> _arrayPool;
private char[] _indentChars;
private Base64Encoder Base64Encoder
{
get
{
if (this._base64Encoder == null)
{
this._base64Encoder = new Base64Encoder(this._writer);
}
return this._base64Encoder;
}
}
/// <summary>
/// Gets or sets the writer's character array pool.
/// </summary>
public IArrayPool<char> ArrayPool
{
get => this._arrayPool;
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
this._arrayPool = value;
}
}
/// <summary>
/// Gets or sets how many <see cref="JsonTextWriter.IndentChar"/>s to write for each level in the hierarchy when <see cref="JsonWriter.Formatting"/> is set to <see cref="Formatting.Indented"/>.
/// </summary>
public int Indentation
{
get => this._indentation;
set
{
if (value < 0)
{
throw new ArgumentException("Indentation value must be greater than 0.");
}
this._indentation = value;
}
}
/// <summary>
/// Gets or sets which character to use to quote attribute values.
/// </summary>
public char QuoteChar
{
get => this._quoteChar;
set
{
if (value != '"' && value != '\'')
{
throw new ArgumentException(@"Invalid JavaScript string quote character. Valid quote characters are ' and "".");
}
this._quoteChar = value;
this.UpdateCharEscapeFlags();
}
}
/// <summary>
/// Gets or sets which character to use for indenting when <see cref="JsonWriter.Formatting"/> is set to <see cref="Formatting.Indented"/>.
/// </summary>
public char IndentChar
{
get => this._indentChar;
set
{
if (value != this._indentChar)
{
this._indentChar = value;
this._indentChars = null;
}
}
}
/// <summary>
/// Gets or sets a value indicating whether object names will be surrounded with quotes.
/// </summary>
public bool QuoteName
{
get => this._quoteName;
set => this._quoteName = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonTextWriter"/> class using the specified <see cref="TextWriter"/>.
/// </summary>
/// <param name="textWriter">The <see cref="TextWriter"/> to write to.</param>
public JsonTextWriter(TextWriter textWriter)
{
if (textWriter == null)
{
throw new ArgumentNullException(nameof(textWriter));
}
this._writer = textWriter;
this._quoteChar = '"';
this._quoteName = true;
this._indentChar = ' ';
this._indentation = 2;
this.UpdateCharEscapeFlags();
#if HAVE_ASYNC
_safeAsync = GetType() == typeof(JsonTextWriter);
#endif
}
/// <summary>
/// Flushes whatever is in the buffer to the underlying <see cref="TextWriter"/> and also flushes the underlying <see cref="TextWriter"/>.
/// </summary>
public override void Flush()
{
this._writer.Flush();
}
/// <summary>
/// Closes this writer.
/// If <see cref="JsonWriter.CloseOutput"/> is set to <c>true</c>, the underlying <see cref="TextWriter"/> is also closed.
/// If <see cref="JsonWriter.AutoCompleteOnClose"/> is set to <c>true</c>, the JSON is auto-completed.
/// </summary>
public override void Close()
{
base.Close();
this.CloseBufferAndWriter();
}
private void CloseBufferAndWriter()
{
if (this._writeBuffer != null)
{
BufferUtils.ReturnBuffer(this._arrayPool, this._writeBuffer);
this._writeBuffer = null;
}
if (this.CloseOutput)
{
#if HAVE_STREAM_READER_WRITER_CLOSE
_writer?.Close();
#else
this._writer?.Dispose();
#endif
}
}
/// <summary>
/// Writes the beginning of a JSON object.
/// </summary>
public override void WriteStartObject()
{
this.InternalWriteStart(JsonToken.StartObject, JsonContainerType.Object);
this._writer.Write('{');
}
/// <summary>
/// Writes the beginning of a JSON array.
/// </summary>
public override void WriteStartArray()
{
this.InternalWriteStart(JsonToken.StartArray, JsonContainerType.Array);
this._writer.Write('[');
}
/// <summary>
/// Writes the start of a constructor with the given name.
/// </summary>
/// <param name="name">The name of the constructor.</param>
public override void WriteStartConstructor(string name)
{
this.InternalWriteStart(JsonToken.StartConstructor, JsonContainerType.Constructor);
this._writer.Write("new ");
this._writer.Write(name);
this._writer.Write('(');
}
/// <summary>
/// Writes the specified end token.
/// </summary>
/// <param name="token">The end token to write.</param>
protected override void WriteEnd(JsonToken token)
{
switch (token)
{
case JsonToken.EndObject:
this._writer.Write('}');
break;
case JsonToken.EndArray:
this._writer.Write(']');
break;
case JsonToken.EndConstructor:
this._writer.Write(')');
break;
default:
throw JsonWriterException.Create(this, "Invalid JsonToken: " + token, null);
}
}
/// <summary>
/// Writes the property name of a name/value pair on a JSON object.
/// </summary>
/// <param name="name">The name of the property.</param>
public override void WritePropertyName(string name)
{
this.InternalWritePropertyName(name);
this.WriteEscapedString(name, this._quoteName);
this._writer.Write(':');
}
/// <summary>
/// Writes the property name of a name/value pair on a JSON object.
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="escape">A flag to indicate whether the text should be escaped when it is written as a JSON property name.</param>
public override void WritePropertyName(string name, bool escape)
{
this.InternalWritePropertyName(name);
if (escape)
{
this.WriteEscapedString(name, this._quoteName);
}
else
{
if (this._quoteName)
{
this._writer.Write(this._quoteChar);
}
this._writer.Write(name);
if (this._quoteName)
{
this._writer.Write(this._quoteChar);
}
}
this._writer.Write(':');
}
internal override void OnStringEscapeHandlingChanged()
{
this.UpdateCharEscapeFlags();
}
private void UpdateCharEscapeFlags()
{
this._charEscapeFlags = JavaScriptUtils.GetCharEscapeFlags(this.StringEscapeHandling, this._quoteChar);
}
/// <summary>
/// Writes indent characters.
/// </summary>
protected override void WriteIndent()
{
// levels of indentation multiplied by the indent count
int currentIndentCount = this.Top * this._indentation;
int newLineLen = this.SetIndentChars();
this._writer.Write(this._indentChars, 0, newLineLen + Math.Min(currentIndentCount, IndentCharBufferSize));
while ((currentIndentCount -= IndentCharBufferSize) > 0)
{
this._writer.Write(this._indentChars, newLineLen, Math.Min(currentIndentCount, IndentCharBufferSize));
}
}
private int SetIndentChars()
{
// Set _indentChars to be a newline followed by IndentCharBufferSize indent characters.
string writerNewLine = this._writer.NewLine;
int newLineLen = writerNewLine.Length;
bool match = this._indentChars != null && this._indentChars.Length == IndentCharBufferSize + newLineLen;
if (match)
{
for (int i = 0; i != newLineLen; ++i)
{
if (writerNewLine[i] != this._indentChars[i])
{
match = false;
break;
}
}
}
if (!match)
{
// If we're here, either _indentChars hasn't been set yet, or _writer.NewLine
// has been changed, or _indentChar has been changed.
this._indentChars = (writerNewLine + new string(this._indentChar, IndentCharBufferSize)).ToCharArray();
}
return newLineLen;
}
/// <summary>
/// Writes the JSON value delimiter.
/// </summary>
protected override void WriteValueDelimiter()
{
this._writer.Write(',');
}
/// <summary>
/// Writes an indent space.
/// </summary>
protected override void WriteIndentSpace()
{
this._writer.Write(' ');
}
private void WriteValueInternal(string value, JsonToken token)
{
this._writer.Write(value);
}
#region WriteValue methods
/// <summary>
/// Writes a <see cref="Object"/> value.
/// An error will raised if the value cannot be written as a single JSON token.
/// </summary>
/// <param name="value">The <see cref="Object"/> value to write.</param>
public override void WriteValue(object value)
{
#if HAVE_BIG_INTEGER
if (value is BigInteger)
{
InternalWriteValue(JsonToken.Integer);
WriteValueInternal(((BigInteger)value).ToString(CultureInfo.InvariantCulture), JsonToken.String);
}
else
#endif
{
base.WriteValue(value);
}
}
/// <summary>
/// Writes a null value.
/// </summary>
public override void WriteNull()
{
this.InternalWriteValue(JsonToken.Null);
this.WriteValueInternal(JsonConvert.Null, JsonToken.Null);
}
/// <summary>
/// Writes an undefined value.
/// </summary>
public override void WriteUndefined()
{
this.InternalWriteValue(JsonToken.Undefined);
this.WriteValueInternal(JsonConvert.Undefined, JsonToken.Undefined);
}
/// <summary>
/// Writes raw JSON.
/// </summary>
/// <param name="json">The raw JSON to write.</param>
public override void WriteRaw(string json)
{
this.InternalWriteRaw();
this._writer.Write(json);
}
/// <summary>
/// Writes a <see cref="String"/> value.
/// </summary>
/// <param name="value">The <see cref="String"/> value to write.</param>
public override void WriteValue(string value)
{
this.InternalWriteValue(JsonToken.String);
if (value == null)
{
this.WriteValueInternal(JsonConvert.Null, JsonToken.Null);
}
else
{
this.WriteEscapedString(value, true);
}
}
private void WriteEscapedString(string value, bool quote)
{
this.EnsureWriteBuffer();
JavaScriptUtils.WriteEscapedJavaScriptString(this._writer, value, this._quoteChar, quote, this._charEscapeFlags, this.StringEscapeHandling, this._arrayPool, ref this._writeBuffer);
}
/// <summary>
/// Writes a <see cref="Int32"/> value.
/// </summary>
/// <param name="value">The <see cref="Int32"/> value to write.</param>
public override void WriteValue(int value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value);
}
/// <summary>
/// Writes a <see cref="UInt32"/> value.
/// </summary>
/// <param name="value">The <see cref="UInt32"/> value to write.</param>
public override void WriteValue(uint value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value);
}
/// <summary>
/// Writes a <see cref="Int64"/> value.
/// </summary>
/// <param name="value">The <see cref="Int64"/> value to write.</param>
public override void WriteValue(long value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value);
}
/// <summary>
/// Writes a <see cref="UInt64"/> value.
/// </summary>
/// <param name="value">The <see cref="UInt64"/> value to write.</param>
public override void WriteValue(ulong value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value, false);
}
/// <summary>
/// Writes a <see cref="Single"/> value.
/// </summary>
/// <param name="value">The <see cref="Single"/> value to write.</param>
public override void WriteValue(float value)
{
this.InternalWriteValue(JsonToken.Float);
this.WriteValueInternal(JsonConvert.ToString(value, this.FloatFormatHandling, this.QuoteChar, false), JsonToken.Float);
}
/// <summary>
/// Writes a <see cref="Nullable{T}"/> of <see cref="Single"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{T}"/> of <see cref="Single"/> value to write.</param>
public override void WriteValue(float? value)
{
if (value == null)
{
this.WriteNull();
}
else
{
this.InternalWriteValue(JsonToken.Float);
this.WriteValueInternal(JsonConvert.ToString(value.GetValueOrDefault(), this.FloatFormatHandling, this.QuoteChar, true), JsonToken.Float);
}
}
/// <summary>
/// Writes a <see cref="Double"/> value.
/// </summary>
/// <param name="value">The <see cref="Double"/> value to write.</param>
public override void WriteValue(double value)
{
this.InternalWriteValue(JsonToken.Float);
this.WriteValueInternal(JsonConvert.ToString(value, this.FloatFormatHandling, this.QuoteChar, false), JsonToken.Float);
}
/// <summary>
/// Writes a <see cref="Nullable{T}"/> of <see cref="Double"/> value.
/// </summary>
/// <param name="value">The <see cref="Nullable{T}"/> of <see cref="Double"/> value to write.</param>
public override void WriteValue(double? value)
{
if (value == null)
{
this.WriteNull();
}
else
{
this.InternalWriteValue(JsonToken.Float);
this.WriteValueInternal(JsonConvert.ToString(value.GetValueOrDefault(), this.FloatFormatHandling, this.QuoteChar, true), JsonToken.Float);
}
}
/// <summary>
/// Writes a <see cref="Boolean"/> value.
/// </summary>
/// <param name="value">The <see cref="Boolean"/> value to write.</param>
public override void WriteValue(bool value)
{
this.InternalWriteValue(JsonToken.Boolean);
this.WriteValueInternal(JsonConvert.ToString(value), JsonToken.Boolean);
}
/// <summary>
/// Writes a <see cref="Int16"/> value.
/// </summary>
/// <param name="value">The <see cref="Int16"/> value to write.</param>
public override void WriteValue(short value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value);
}
/// <summary>
/// Writes a <see cref="UInt16"/> value.
/// </summary>
/// <param name="value">The <see cref="UInt16"/> value to write.</param>
public override void WriteValue(ushort value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value);
}
/// <summary>
/// Writes a <see cref="Char"/> value.
/// </summary>
/// <param name="value">The <see cref="Char"/> value to write.</param>
public override void WriteValue(char value)
{
this.InternalWriteValue(JsonToken.String);
this.WriteValueInternal(JsonConvert.ToString(value), JsonToken.String);
}
/// <summary>
/// Writes a <see cref="Byte"/> value.
/// </summary>
/// <param name="value">The <see cref="Byte"/> value to write.</param>
public override void WriteValue(byte value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value);
}
/// <summary>
/// Writes a <see cref="SByte"/> value.
/// </summary>
/// <param name="value">The <see cref="SByte"/> value to write.</param>
public override void WriteValue(sbyte value)
{
this.InternalWriteValue(JsonToken.Integer);
this.WriteIntegerValue(value);
}
/// <summary>
/// Writes a <see cref="Decimal"/> value.
/// </summary>
/// <param name="value">The <see cref="Decimal"/> value to write.</param>
public override void WriteValue(decimal value)
{
this.InternalWriteValue(JsonToken.Float);
this.WriteValueInternal(JsonConvert.ToString(value), JsonToken.Float);
}
/// <summary>
/// Writes a <see cref="DateTime"/> value.
/// </summary>
/// <param name="value">The <see cref="DateTime"/> value to write.</param>
public override void WriteValue(DateTime value)
{
this.InternalWriteValue(JsonToken.Date);
value = DateTimeUtils.EnsureDateTime(value, this.DateTimeZoneHandling);
if (string.IsNullOrEmpty(this.DateFormatString))
{
int length = this.WriteValueToBuffer(value);
this._writer.Write(this._writeBuffer, 0, length);
}
else
{
this._writer.Write(this._quoteChar);
this._writer.Write(value.ToString(this.DateFormatString, this.Culture));
this._writer.Write(this._quoteChar);
}
}
private int WriteValueToBuffer(DateTime value)
{
this.EnsureWriteBuffer();
int pos = 0;
this._writeBuffer[pos++] = this._quoteChar;
pos = DateTimeUtils.WriteDateTimeString(this._writeBuffer, pos, value, null, value.Kind, this.DateFormatHandling);
this._writeBuffer[pos++] = this._quoteChar;
return pos;
}
/// <summary>
/// Writes a <see cref="Byte"/>[] value.
/// </summary>
/// <param name="value">The <see cref="Byte"/>[] value to write.</param>
public override void WriteValue(byte[] value)
{
if (value == null)
{
this.WriteNull();
}
else
{
this.InternalWriteValue(JsonToken.Bytes);
this._writer.Write(this._quoteChar);
this.Base64Encoder.Encode(value, 0, value.Length);
this.Base64Encoder.Flush();
this._writer.Write(this._quoteChar);
}
}
#if HAVE_DATE_TIME_OFFSET
/// <summary>
/// Writes a <see cref="DateTimeOffset"/> value.
/// </summary>
/// <param name="value">The <see cref="DateTimeOffset"/> value to write.</param>
public override void WriteValue(DateTimeOffset value)
{
InternalWriteValue(JsonToken.Date);
if (string.IsNullOrEmpty(DateFormatString))
{
int length = WriteValueToBuffer(value);
_writer.Write(_writeBuffer, 0, length);
}
else
{
_writer.Write(_quoteChar);
_writer.Write(value.ToString(DateFormatString, Culture));
_writer.Write(_quoteChar);
}
}
private int WriteValueToBuffer(DateTimeOffset value)
{
EnsureWriteBuffer();
int pos = 0;
_writeBuffer[pos++] = _quoteChar;
pos = DateTimeUtils.WriteDateTimeString(_writeBuffer, pos, (DateFormatHandling == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, DateFormatHandling);
_writeBuffer[pos++] = _quoteChar;
return pos;
}
#endif
/// <summary>
/// Writes a <see cref="Guid"/> value.
/// </summary>
/// <param name="value">The <see cref="Guid"/> value to write.</param>
public override void WriteValue(Guid value)
{
this.InternalWriteValue(JsonToken.String);
string text = null;
#if HAVE_CHAR_TO_STRING_WITH_CULTURE
text = value.ToString("D", CultureInfo.InvariantCulture);
#else
text = value.ToString("D");
#endif
this._writer.Write(this._quoteChar);
this._writer.Write(text);
this._writer.Write(this._quoteChar);
}
/// <summary>
/// Writes a <see cref="TimeSpan"/> value.
/// </summary>
/// <param name="value">The <see cref="TimeSpan"/> value to write.</param>
public override void WriteValue(TimeSpan value)
{
this.InternalWriteValue(JsonToken.String);
string text;
#if !HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE
text = value.ToString();
#else
text = value.ToString(null, CultureInfo.InvariantCulture);
#endif
this._writer.Write(this._quoteChar);
this._writer.Write(text);
this._writer.Write(this._quoteChar);
}
/// <summary>
/// Writes a <see cref="Uri"/> value.
/// </summary>
/// <param name="value">The <see cref="Uri"/> value to write.</param>
public override void WriteValue(Uri value)
{
if (value == null)
{
this.WriteNull();
}
else
{
this.InternalWriteValue(JsonToken.String);
this.WriteEscapedString(value.OriginalString, true);
}
}
#endregion WriteValue methods
/// <summary>
/// Writes a comment <c>/*...*/</c> containing the specified text.
/// </summary>
/// <param name="text">Text to place inside the comment.</param>
public override void WriteComment(string text)
{
this.InternalWriteComment();
this._writer.Write("/*");
this._writer.Write(text);
this._writer.Write("*/");
}
/// <summary>
/// Writes the given white space.
/// </summary>
/// <param name="ws">The string of white space characters.</param>
public override void WriteWhitespace(string ws)
{
this.InternalWriteWhitespace(ws);
this._writer.Write(ws);
}
private void EnsureWriteBuffer()
{
if (this._writeBuffer == null)
{
// maximum buffer sized used when writing iso date
this._writeBuffer = BufferUtils.RentBuffer(this._arrayPool, 35);
}
}
private void WriteIntegerValue(long value)
{
if (value >= 0 && value <= 9)
{
this._writer.Write((char)('0' + value));
}
else
{
bool negative = value < 0;
this.WriteIntegerValue(negative ? (ulong)-value : (ulong)value, negative);
}
}
private void WriteIntegerValue(ulong value, bool negative)
{
if (!negative & value <= 9)
{
this._writer.Write((char)('0' + value));
}
else
{
int length = this.WriteNumberToBuffer(value, negative);
this._writer.Write(this._writeBuffer, 0, length);
}
}
private int WriteNumberToBuffer(ulong value, bool negative)
{
if (value <= uint.MaxValue)
{
// avoid the 64 bit division if possible
return this.WriteNumberToBuffer((uint)value, negative);
}
this.EnsureWriteBuffer();
int totalLength = MathUtils.IntLength(value);
if (negative)
{
totalLength++;
this._writeBuffer[0] = '-';
}
int index = totalLength;
do
{
ulong quotient = value / 10;
ulong digit = value - (quotient * 10);
this._writeBuffer[--index] = (char)('0' + digit);
value = quotient;
} while (value != 0);
return totalLength;
}
private void WriteIntegerValue(int value)
{
if (value >= 0 && value <= 9)
{
this._writer.Write((char)('0' + value));
}
else
{
bool negative = value < 0;
this.WriteIntegerValue(negative ? (uint)-value : (uint)value, negative);
}
}
private void WriteIntegerValue(uint value, bool negative)
{
if (!negative & value <= 9)
{
this._writer.Write((char)('0' + value));
}
else
{
int length = this.WriteNumberToBuffer(value, negative);
this._writer.Write(this._writeBuffer, 0, length);
}
}
private int WriteNumberToBuffer(uint value, bool negative)
{
this.EnsureWriteBuffer();
int totalLength = MathUtils.IntLength(value);
if (negative)
{
totalLength++;
this._writeBuffer[0] = '-';
}
int index = totalLength;
do
{
uint quotient = value / 10;
uint digit = value - (quotient * 10);
this._writeBuffer[--index] = (char)('0' + digit);
value = quotient;
} while (value != 0);
return totalLength;
}
}
}