diff --git a/src/LuYao.Common/Text/Json/JsonReader.cs b/src/LuYao.Common/Text/Json/JsonReader.cs
deleted file mode 100644
index 0f4ed31..0000000
--- a/src/LuYao.Common/Text/Json/JsonReader.cs
+++ /dev/null
@@ -1,442 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace LuYao.Text.Json;
-
-///
-/// 高性能 JSON 读取器,使用固定大小缓冲区优化性能
-///
-public sealed class JsonReader : IDisposable
-{
- private readonly TextReader _reader;
- private readonly bool _ownsReader;
- private char[] _buffer;
- private int _bufferPosition;
- private int _bufferLength;
- private int _currentIndex;
- private int _line = 1;
- private int _column = 1;
- private bool _disposed;
-
- private const int DefaultBufferSize = 4096;
-
- ///
- /// 初始化 JsonReader 实例
- ///
- /// 要读取的 TextReader
- /// 是否拥有并释放 TextReader
- /// 缓冲区大小
- public JsonReader(TextReader reader, bool ownsReader = false, int bufferSize = DefaultBufferSize)
- {
- _reader = reader ?? throw new ArgumentNullException(nameof(reader));
- _ownsReader = ownsReader;
- _buffer = new char[Math.Max(bufferSize, 64)];
- }
-
- ///
- /// 使用 JSON 字符串初始化 JsonReader 实例
- ///
- /// 要解析的 JSON 字符串
- public JsonReader(string json) : this(new StringReader(json), true)
- {
- }
-
- ///
- /// 当前标记类型
- ///
- public JsonTokenType TokenType { get; private set; } = JsonTokenType.None;
-
- ///
- /// 当前标记值
- ///
- public object? Value { get; private set; }
-
- ///
- /// 当前行号
- ///
- public int Line => _line;
-
- ///
- /// 当前列号
- ///
- public int Column => _column;
-
- ///
- /// 读取下一个标记
- ///
- /// 如果成功读取到标记则返回 true,否则返回 false
- public bool Read()
- {
- if (_disposed) throw new ObjectDisposedException(nameof(JsonReader));
-
- SkipWhitespace();
-
- if (!EnsureBuffer())
- {
- TokenType = JsonTokenType.None;
- Value = null;
- return false;
- }
-
- char c = _buffer[_bufferPosition];
-
- switch (c)
- {
- case '{':
- TokenType = JsonTokenType.StartObject;
- Value = null;
- ConsumeChar();
- return true;
-
- case '}':
- TokenType = JsonTokenType.EndObject;
- Value = null;
- ConsumeChar();
- return true;
-
- case '[':
- TokenType = JsonTokenType.StartArray;
- Value = null;
- ConsumeChar();
- return true;
-
- case ']':
- TokenType = JsonTokenType.EndArray;
- Value = null;
- ConsumeChar();
- return true;
-
- case '"':
- return ReadString();
-
- case 't':
- case 'f':
- return ReadBoolean();
-
- case 'n':
- return ReadNull();
-
- case ',':
- case ':':
- ConsumeChar();
- return Read(); // 跳过分隔符,读取下一个标记
-
- default:
- if (IsDigit(c) || c == '-' || c == '+')
- return ReadNumber();
-
- throw new JsonException($"Unexpected character '{c}' at line {_line}, column {_column}");
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void SkipWhitespace()
- {
- while (EnsureBuffer())
- {
- char c = _buffer[_bufferPosition];
- if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
- {
- ConsumeChar();
- }
- else
- {
- break;
- }
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ConsumeChar()
- {
- if (_buffer[_bufferPosition] == '\n')
- {
- _line++;
- _column = 1;
- }
- else
- {
- _column++;
- }
-
- _bufferPosition++;
- _currentIndex++;
- }
-
- private bool EnsureBuffer()
- {
- if (_bufferPosition >= _bufferLength)
- {
- _bufferLength = _reader.Read(_buffer, 0, _buffer.Length);
- _bufferPosition = 0;
-
- if (_bufferLength == 0)
- return false;
- }
- return true;
- }
-
- private bool ReadString()
- {
- ConsumeChar(); // 消费开始的引号
-
- var sb = new StringBuilder();
-
- while (EnsureBuffer())
- {
- char c = _buffer[_bufferPosition];
-
- if (c == '"')
- {
- ConsumeChar(); // 消费结束的引号
-
- // 判断是否为属性名(简化判断:查看下一个非空白字符是否为冒号)
- int savedPos = _bufferPosition;
- int savedLine = _line;
- int savedColumn = _column;
-
- SkipWhitespace();
- bool isPropertyName = EnsureBuffer() && _buffer[_bufferPosition] == ':';
-
- // 恢复位置和行列号
- _bufferPosition = savedPos;
- _line = savedLine;
- _column = savedColumn;
-
- TokenType = isPropertyName ? JsonTokenType.PropertyName : JsonTokenType.String;
- Value = sb.ToString();
- return true;
- }
- else if (c == '\\')
- {
- ConsumeChar();
- if (!EnsureBuffer())
- throw new JsonException("Unexpected end of input while reading string escape sequence");
-
- char escaped = _buffer[_bufferPosition];
- ConsumeChar();
-
- switch (escaped)
- {
- case '"': sb.Append('"'); break;
- case '\\': sb.Append('\\'); break;
- case '/': sb.Append('/'); break;
- case 'b': sb.Append('\b'); break;
- case 'f': sb.Append('\f'); break;
- case 'n': sb.Append('\n'); break;
- case 'r': sb.Append('\r'); break;
- case 't': sb.Append('\t'); break;
- case 'u':
- // Unicode 转义序列 \uXXXX
- string hexDigits = ReadHexDigits(4);
- if (int.TryParse(hexDigits, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int unicode))
- {
- sb.Append((char)unicode);
- }
- else
- {
- throw new JsonException($"Invalid unicode escape sequence \\u{hexDigits}");
- }
- break;
- default:
- throw new JsonException($"Invalid escape sequence \\{escaped}");
- }
- }
- else if (char.IsControl(c))
- {
- throw new JsonException($"Unescaped control character in string at line {_line}, column {_column}");
- }
- else
- {
- sb.Append(c);
- ConsumeChar();
- }
- }
-
- throw new JsonException("Unexpected end of input while reading string");
- }
-
- private string ReadHexDigits(int count)
- {
- var sb = new StringBuilder(count);
- for (int i = 0; i < count; i++)
- {
- if (!EnsureBuffer())
- throw new JsonException("Unexpected end of input while reading unicode escape sequence");
-
- char c = _buffer[_bufferPosition];
- if (!IsHexDigit(c))
- throw new JsonException($"Invalid hex digit '{c}' in unicode escape sequence");
-
- sb.Append(c);
- ConsumeChar();
- }
- return sb.ToString();
- }
-
- private bool ReadNumber()
- {
- var sb = new StringBuilder();
- bool hasDecimalPoint = false;
- bool hasExponent = false;
-
- // 处理负号
- if (EnsureBuffer() && (_buffer[_bufferPosition] == '-' || _buffer[_bufferPosition] == '+'))
- {
- sb.Append(_buffer[_bufferPosition]);
- ConsumeChar();
- }
-
- // 读取数字部分
- bool hasDigits = false;
- while (EnsureBuffer())
- {
- char c = _buffer[_bufferPosition];
-
- if (IsDigit(c))
- {
- sb.Append(c);
- hasDigits = true;
- ConsumeChar();
- }
- else if (c == '.' && !hasDecimalPoint && !hasExponent)
- {
- hasDecimalPoint = true;
- sb.Append(c);
- ConsumeChar();
- }
- else if ((c == 'e' || c == 'E') && !hasExponent && hasDigits)
- {
- hasExponent = true;
- sb.Append(c);
- ConsumeChar();
-
- // 指数部分可能有符号
- if (EnsureBuffer() && (_buffer[_bufferPosition] == '+' || _buffer[_bufferPosition] == '-'))
- {
- sb.Append(_buffer[_bufferPosition]);
- ConsumeChar();
- }
- }
- else
- {
- break;
- }
- }
-
- if (!hasDigits)
- throw new JsonException($"Invalid number format at line {_line}, column {_column}");
-
- string numberStr = sb.ToString();
- TokenType = JsonTokenType.Number;
-
- // 尝试解析为不同的数字类型
- if (!hasDecimalPoint && !hasExponent)
- {
- if (long.TryParse(numberStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out long longValue))
- {
- Value = longValue;
- return true;
- }
- }
-
- if (double.TryParse(numberStr, NumberStyles.Float, CultureInfo.InvariantCulture, out double doubleValue))
- {
- Value = doubleValue;
- return true;
- }
-
- throw new JsonException($"Invalid number format '{numberStr}' at line {_line}, column {_column}");
- }
-
- private bool ReadBoolean()
- {
- if (ReadLiteral("true"))
- {
- TokenType = JsonTokenType.Boolean;
- Value = true;
- return true;
- }
-
- if (ReadLiteral("false"))
- {
- TokenType = JsonTokenType.Boolean;
- Value = false;
- return true;
- }
-
- throw new JsonException($"Invalid boolean value at line {_line}, column {_column}");
- }
-
- private bool ReadNull()
- {
- if (ReadLiteral("null"))
- {
- TokenType = JsonTokenType.Null;
- Value = null;
- return true;
- }
-
- throw new JsonException($"Invalid null value at line {_line}, column {_column}");
- }
-
- private bool ReadLiteral(string literal)
- {
- for (int i = 0; i < literal.Length; i++)
- {
- if (!EnsureBuffer() || _buffer[_bufferPosition] != literal[i])
- return false;
- ConsumeChar();
- }
- return true;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsDigit(char c) => c >= '0' && c <= '9';
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsHexDigit(char c) =>
- (c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'F') ||
- (c >= 'a' && c <= 'f');
-
- ///
- /// 释放资源
- ///
- public void Dispose()
- {
- if (!_disposed)
- {
- _buffer = null!;
-
- if (_ownsReader)
- {
- _reader?.Dispose();
- }
-
- _disposed = true;
- }
- }
-}
-
-///
-/// JSON 解析异常
-///
-public class JsonException : Exception
-{
- ///
- /// 使用错误消息初始化 JsonException 实例
- ///
- /// 错误消息
- public JsonException(string message) : base(message) { }
-
- ///
- /// 使用错误消息和内部异常初始化 JsonException 实例
- ///
- /// 错误消息
- /// 内部异常
- public JsonException(string message, Exception innerException) : base(message, innerException) { }
-}
\ No newline at end of file
diff --git a/src/LuYao.Common/Text/Json/JsonWriter.cs b/src/LuYao.Common/Text/Json/JsonWriter.cs
deleted file mode 100644
index f86698d..0000000
--- a/src/LuYao.Common/Text/Json/JsonWriter.cs
+++ /dev/null
@@ -1,431 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace LuYao.Text.Json;
-
-///
-/// 高性能 JSON 写入器,使用固定大小缓冲区和 Span 优化
-///
-public sealed class JsonWriter : IDisposable
-{
- private readonly TextWriter _writer;
- private readonly bool _ownsWriter;
- private char[]? _buffer;
- private int _bufferPosition;
- private bool _disposed;
-
- private const int DefaultBufferSize = 4096;
-
- // JSON 状态栈,用于管理嵌套结构
- private JsonWriteState[] _stateStack;
- private int _stackDepth;
-
- // 格式化选项
- private readonly bool _indented;
- private readonly string _indentString;
- private readonly bool _spaceAfterComma;
- private int _currentIndentLevel;
-
- ///
- /// 初始化 JsonWriter 实例
- ///
- /// 要写入的 TextWriter
- /// 是否拥有并释放 TextWriter
- /// 是否启用缩进格式化
- /// 缩进字符串
- /// 是否在逗号后添加空格(仅在非缩进模式下)
- /// 缓冲区大小
- public JsonWriter(TextWriter writer, bool ownsWriter = false, bool indented = false,
- string indentString = " ", bool spaceAfterComma = false, int bufferSize = DefaultBufferSize)
- {
- _writer = writer ?? throw new ArgumentNullException(nameof(writer));
- _ownsWriter = ownsWriter;
- _indented = indented;
- _indentString = indentString ?? " ";
- _spaceAfterComma = spaceAfterComma && !indented; // 缩进模式下忽略此选项
- _buffer = new char[Math.Max(bufferSize, 64)];
- _stateStack = new JsonWriteState[8]; // 初始栈大小
- }
-
- ///
- /// 使用 StringBuilder 初始化 JsonWriter 实例
- ///
- /// 要写入的 StringBuilder
- /// 是否启用缩进格式化
- /// 缩进字符串
- /// 是否在逗号后添加空格(仅在非缩进模式下)
- public JsonWriter(StringBuilder sb, bool indented = false, string indentString = " ", bool spaceAfterComma = false)
- : this(new StringWriter(sb), true, indented, indentString, spaceAfterComma)
- {
- }
-
- private enum JsonWriteState
- {
- Start,
- Object,
- Array,
- Property,
- Value
- }
-
- ///
- /// 写入对象开始符 {
- ///
- public void WriteStartObject()
- {
- WriteValueSeparator();
- WriteIndentIfNeeded();
- WriteChar('{');
- PushState(JsonWriteState.Object);
- IncreaseIndent();
- if (_indented) WriteNewLine(); // 在增加缩进级别后再换行
- }
-
- ///
- /// 写入对象结束符 }
- ///
- public void WriteEndObject()
- {
- PopState(JsonWriteState.Object);
- DecreaseIndent();
- if (_indented) WriteNewLine(); // 先换行再写缩进
- WriteIndentIfNeeded();
- WriteChar('}');
- }
-
- ///
- /// 写入数组开始符 [
- ///
- public void WriteStartArray()
- {
- WriteValueSeparator();
- WriteChar('[');
- PushState(JsonWriteState.Array);
- IncreaseIndent();
- if (_indented) WriteNewLine(); // 在增加缩进级别后再换行
- }
-
- ///
- /// 写入数组结束符 ]
- ///
- public void WriteEndArray()
- {
- PopState(JsonWriteState.Array);
- DecreaseIndent();
- if (_indented) WriteNewLine(); // 先换行再写缩进
- WriteIndentIfNeeded();
- WriteChar(']');
- }
-
- ///
- /// 写入属性名
- ///
- public void WritePropertyName(string? name)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- WriteValueSeparator();
- WriteIndentIfNeeded();
- WriteStringEscaped(name);
- WriteChar(':');
- if (_indented) WriteChar(' ');
-
- SetState(JsonWriteState.Property);
- }
-
- ///
- /// 写入字符串值
- ///
- public void WriteValue(string? value)
- {
- WriteValueSeparator();
-
- if (value == null)
- {
- WriteNull();
- }
- else
- {
- WriteStringEscaped(value);
- }
-
- SetState(JsonWriteState.Value);
- }
-
- ///
- /// 写入整数值
- ///
- public void WriteValue(long value)
- {
- WriteValueSeparator();
- WriteIndentIfNeeded();
- WriteString(value.ToString(CultureInfo.InvariantCulture));
- SetState(JsonWriteState.Value);
- }
-
- ///
- /// 写入浮点值
- ///
- public void WriteValue(double value)
- {
- WriteValueSeparator();
- WriteIndentIfNeeded();
-
- if (double.IsNaN(value) || double.IsInfinity(value))
- {
- WriteNull();
- }
- else
- {
- WriteString(value.ToString("R", CultureInfo.InvariantCulture));
- }
-
- SetState(JsonWriteState.Value);
- }
-
- ///
- /// 写入布尔值
- ///
- public void WriteValue(bool value)
- {
- WriteValueSeparator();
- WriteIndentIfNeeded();
- WriteString(value ? "true" : "false");
- SetState(JsonWriteState.Value);
- }
-
- ///
- /// 写入 null 值
- ///
- public void WriteNull()
- {
- WriteValueSeparator();
- WriteIndentIfNeeded();
- WriteString("null");
- SetState(JsonWriteState.Value);
- }
-
- ///
- /// 写入原始 JSON
- ///
- public void WriteRaw(string? json)
- {
- if (json == null)
- throw new ArgumentNullException(nameof(json));
-
- WriteValueSeparator();
- WriteIndentIfNeeded();
- WriteString(json);
- SetState(JsonWriteState.Value);
- }
-
- ///
- /// 刷新缓冲区
- ///
- public void Flush()
- {
- if (_disposed || _buffer is null)
- return;
-
- if (_bufferPosition > 0)
- {
- _writer.Write(_buffer, 0, _bufferPosition);
- _bufferPosition = 0;
- }
- _writer.Flush();
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteChar(char c)
- {
- EnsureCapacity(1);
- _buffer![_bufferPosition++] = c;
- }
-
- ///
- /// 写入原始字符串,不进行转义
- ///
- private void WriteString(string? str)
- {
- if (str == null) return;
-
- EnsureCapacity(str.Length);
- for (int i = 0; i < str.Length; i++)
- {
- _buffer![_bufferPosition++] = str[i];
- }
- }
-
- ///
- /// 写入字符串,进行JSON转义
- ///
- private void WriteStringEscaped(string value)
- {
- if (value == null) return;
-
- WriteChar('"');
-
- foreach (char c in value)
- {
- switch (c)
- {
- case '"':
- WriteString("\\\"");
- break;
- case '\\':
- WriteString("\\\\");
- break;
- case '\b':
- WriteString("\\b");
- break;
- case '\f':
- WriteString("\\f");
- break;
- case '\n':
- WriteString("\\n");
- break;
- case '\r':
- WriteString("\\r");
- break;
- case '\t':
- WriteString("\\t");
- break;
- default:
- if (char.IsControl(c))
- {
- WriteString($"\\u{(int)c:x4}");
- }
- else
- {
- WriteChar(c);
- }
- break;
- }
- }
-
- WriteChar('"');
- }
-
- private void EnsureCapacity(int count)
- {
- if (_bufferPosition + count >= _buffer!.Length)
- {
- Flush();
- }
- }
-
- private void WriteValueSeparator()
- {
- if (_stackDepth > 0)
- {
- var currentState = _stateStack[_stackDepth - 1];
- if (currentState == JsonWriteState.Array || currentState == JsonWriteState.Object)
- {
- // 第一个元素不需要逗号
- _stateStack[_stackDepth - 1] = JsonWriteState.Value;
- }
- else if (currentState == JsonWriteState.Value)
- {
- WriteChar(',');
- if (_indented)
- {
- WriteNewLine();
- }
- else if (_spaceAfterComma)
- {
- WriteChar(' ');
- }
- }
- // Property 状态不需要逗号,只需要设置为 Value
- else if (currentState == JsonWriteState.Property)
- {
- // 属性名后面跟值,不需要逗号
- }
- }
- }
-
- private void WriteIndentIfNeeded()
- {
- if (_indented && _currentIndentLevel > 0)
- {
- for (int i = 0; i < _currentIndentLevel; i++)
- {
- WriteString(_indentString);
- }
- }
- }
-
- private void WriteNewLine()
- {
- WriteChar('\n');
- }
-
- private void PushState(JsonWriteState state)
- {
- if (_stackDepth >= _stateStack.Length)
- {
- Array.Resize(ref _stateStack, _stateStack.Length * 2);
- }
- _stateStack[_stackDepth++] = state;
- }
-
- private void PopState(JsonWriteState expectedState)
- {
- if (_stackDepth == 0)
- throw new InvalidOperationException("Invalid JSON structure: unexpected end marker");
-
- var currentState = _stateStack[--_stackDepth];
- // 允许从 Value 或 Property 状态退出 Object/Array,因为这些都是有效的状态
- if (currentState != expectedState && currentState != JsonWriteState.Value &&
- !(expectedState == JsonWriteState.Object && currentState == JsonWriteState.Property))
- throw new InvalidOperationException($"Invalid JSON structure: expected {expectedState}, got {currentState}");
- }
-
- private void SetState(JsonWriteState state)
- {
- if (_stackDepth > 0)
- {
- _stateStack[_stackDepth - 1] = state;
- }
- }
-
- private void IncreaseIndent()
- {
- if (_indented)
- {
- _currentIndentLevel++;
- }
- }
-
- private void DecreaseIndent()
- {
- if (_indented)
- {
- _currentIndentLevel--;
- }
- }
-
- ///
- /// 释放资源
- ///
- public void Dispose()
- {
- if (!_disposed)
- {
- Flush();
-
- _buffer = null;
-
- if (_ownsWriter)
- {
- _writer?.Dispose();
- }
-
- _disposed = true;
- }
- }
-}
\ No newline at end of file
diff --git a/tests/LuYao.Common.UnitTests/Text/Json/JsonReaderTest.cs b/tests/LuYao.Common.UnitTests/Text/Json/JsonReaderTest.cs
deleted file mode 100644
index d5b12db..0000000
--- a/tests/LuYao.Common.UnitTests/Text/Json/JsonReaderTest.cs
+++ /dev/null
@@ -1,567 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace LuYao.Text.Json;
-
-[TestClass]
-public class JsonReaderTest
-{
- [TestMethod]
- public void Constructor_WithTextReader_ShouldInitializeCorrectly()
- {
- // Arrange
- var textReader = new StringReader("{}");
-
- // Act
- using var jsonReader = new JsonReader(textReader);
-
- // Assert
- Assert.AreEqual(JsonTokenType.None, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
- Assert.AreEqual(1, jsonReader.Line);
- Assert.AreEqual(1, jsonReader.Column);
- }
-
- [TestMethod]
- public void Constructor_WithString_ShouldInitializeCorrectly()
- {
- // Arrange
- string json = "{}";
-
- // Act
- using var jsonReader = new JsonReader(json);
-
- // Assert
- Assert.AreEqual(JsonTokenType.None, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
- Assert.AreEqual(1, jsonReader.Line);
- Assert.AreEqual(1, jsonReader.Column);
- }
-
- [TestMethod]
- public void Constructor_WithNullTextReader_ShouldThrowArgumentNullException()
- {
- // Act & Assert
- Assert.Throws(() => new JsonReader((TextReader)null));
- }
-
- [TestMethod]
- public void Read_EmptyObject_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("{}");
-
- // Act & Assert
- Assert.IsTrue(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.StartObject, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.EndObject, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
-
- Assert.IsFalse(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.None, jsonReader.TokenType);
- }
-
- [TestMethod]
- public void Read_EmptyArray_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("[]");
-
- // Act & Assert
- Assert.IsTrue(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.StartArray, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.EndArray, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
-
- Assert.IsFalse(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.None, jsonReader.TokenType);
- }
-
- [TestMethod]
- public void Read_SimpleString_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"hello\"");
-
- // Act & Assert
- Assert.IsTrue(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.String, jsonReader.TokenType);
- Assert.AreEqual("hello", jsonReader.Value);
-
- Assert.IsFalse(jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_PropertyName_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("{\"name\":\"value\"}");
-
- // Act & Assert
- Assert.IsTrue(jsonReader.Read()); // {
- Assert.AreEqual(JsonTokenType.StartObject, jsonReader.TokenType);
-
- Assert.IsTrue(jsonReader.Read()); // "name"
- Assert.AreEqual(JsonTokenType.PropertyName, jsonReader.TokenType);
- Assert.AreEqual("name", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // "value"
- Assert.AreEqual(JsonTokenType.String, jsonReader.TokenType);
- Assert.AreEqual("value", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // }
- Assert.AreEqual(JsonTokenType.EndObject, jsonReader.TokenType);
- }
-
- [TestMethod]
- public void Read_StringWithEscapes_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"hello\\nworld\\t\\\"test\\\"\"");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.String, jsonReader.TokenType);
- Assert.AreEqual("hello\nworld\t\"test\"", jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_StringWithUnicodeEscape_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"\\u0048\\u0065\\u006C\\u006C\\u006F\""); // "Hello"
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.String, jsonReader.TokenType);
- Assert.AreEqual("Hello", jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_StringWithInvalidUnicodeEscape_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"\\uGGGG\"");
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_StringWithInvalidEscape_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"\\x\"");
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_UnterminatedString_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"unterminated");
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_PositiveInteger_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("123");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(123L, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_NegativeInteger_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("-456");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(-456L, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_PositiveDecimal_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("123.45");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(123.45, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_NegativeDecimal_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("-123.45");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(-123.45, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_NumberWithExponent_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("1.23e4");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(12300.0, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_NumberWithNegativeExponent_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("1.23e-4");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(0.000123, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_InvalidNumber_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("-");
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_BooleanTrue_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("true");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Boolean, jsonReader.TokenType);
- Assert.AreEqual(true, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_BooleanFalse_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("false");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Boolean, jsonReader.TokenType);
- Assert.AreEqual(false, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_InvalidBoolean_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("tru");
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_Null_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("null");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Null, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_InvalidNull_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("nul");
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_ComplexObject_ShouldReadCorrectly()
- {
- // Arrange
- string json = "{\"name\":\"John\",\"age\":30,\"active\":true,\"scores\":[85,92,78],\"address\":null}";
- using var jsonReader = new JsonReader(json);
-
- // Act & Assert
- Assert.IsTrue(jsonReader.Read()); // {
- Assert.AreEqual(JsonTokenType.StartObject, jsonReader.TokenType);
-
- Assert.IsTrue(jsonReader.Read()); // "name"
- Assert.AreEqual(JsonTokenType.PropertyName, jsonReader.TokenType);
- Assert.AreEqual("name", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // "John"
- Assert.AreEqual(JsonTokenType.String, jsonReader.TokenType);
- Assert.AreEqual("John", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // "age"
- Assert.AreEqual(JsonTokenType.PropertyName, jsonReader.TokenType);
- Assert.AreEqual("age", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // 30
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(30L, jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // "active"
- Assert.AreEqual(JsonTokenType.PropertyName, jsonReader.TokenType);
- Assert.AreEqual("active", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // true
- Assert.AreEqual(JsonTokenType.Boolean, jsonReader.TokenType);
- Assert.AreEqual(true, jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // "scores"
- Assert.AreEqual(JsonTokenType.PropertyName, jsonReader.TokenType);
- Assert.AreEqual("scores", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // [
- Assert.AreEqual(JsonTokenType.StartArray, jsonReader.TokenType);
-
- Assert.IsTrue(jsonReader.Read()); // 85
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(85L, jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // 92
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(92L, jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // 78
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(78L, jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // ]
- Assert.AreEqual(JsonTokenType.EndArray, jsonReader.TokenType);
-
- Assert.IsTrue(jsonReader.Read()); // "address"
- Assert.AreEqual(JsonTokenType.PropertyName, jsonReader.TokenType);
- Assert.AreEqual("address", jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // null
- Assert.AreEqual(JsonTokenType.Null, jsonReader.TokenType);
- Assert.IsNull(jsonReader.Value);
-
- Assert.IsTrue(jsonReader.Read()); // }
- Assert.AreEqual(JsonTokenType.EndObject, jsonReader.TokenType);
-
- Assert.IsFalse(jsonReader.Read()); // EOF
- Assert.AreEqual(JsonTokenType.None, jsonReader.TokenType);
- }
-
- [TestMethod]
- public void Read_WithWhitespace_ShouldSkipWhitespace()
- {
- // Arrange
- using var jsonReader = new JsonReader(" \t\r\n { \t\r\n } \t\r\n ");
-
- // Act & Assert
- Assert.IsTrue(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.StartObject, jsonReader.TokenType);
-
- Assert.IsTrue(jsonReader.Read());
- Assert.AreEqual(JsonTokenType.EndObject, jsonReader.TokenType);
-
- Assert.IsFalse(jsonReader.Read());
- }
-
- [TestMethod]
- public void LineAndColumn_ShouldUpdateCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("{\n \"name\": \"value\"\n}");
-
- // Act & Assert
- Assert.AreEqual(1, jsonReader.Line);
- Assert.AreEqual(1, jsonReader.Column);
-
- Assert.IsTrue(jsonReader.Read()); // {
- Assert.AreEqual(1, jsonReader.Line);
- Assert.AreEqual(2, jsonReader.Column);
-
- Assert.IsTrue(jsonReader.Read()); // "name"
- Assert.AreEqual(2, jsonReader.Line);
-
- Assert.IsTrue(jsonReader.Read()); // "value"
-
- Assert.IsTrue(jsonReader.Read()); // }
- Assert.AreEqual(3, jsonReader.Line);
- }
-
- [TestMethod]
- public void Read_UnexpectedCharacter_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("@");
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_AfterDispose_ShouldThrowObjectDisposedException()
- {
- // Arrange
- var jsonReader = new JsonReader("{}");
- jsonReader.Dispose();
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Dispose_ShouldDisposeCorrectly()
- {
- // Arrange
- var textReader = new StringReader("{}");
- var jsonReader = new JsonReader(textReader, ownsReader: true);
-
- // Act
- jsonReader.Dispose();
-
- // Assert - 第二次调用不应该抛出异常
- jsonReader.Dispose();
- }
-
- [TestMethod]
- public void Dispose_WithoutOwningReader_ShouldNotDisposeReader()
- {
- // Arrange
- var textReader = new StringReader("{}");
- var jsonReader = new JsonReader(textReader, ownsReader: false);
-
- // Act
- jsonReader.Dispose();
-
- // Assert - Reader 应该仍然可用
- // 注意:这里我们无法直接验证 TextReader 未被释放,因为 StringReader 没有公共的 IsDisposed 属性
- // 但我们可以确保 JsonReader 正确处理了 ownsReader 参数
- }
-
- [TestMethod]
- public void Read_LargeNumber_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("9223372036854775807"); // long.MaxValue
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(long.MaxValue, jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_VeryLargeNumber_ShouldReadAsDouble()
- {
- // Arrange
- using var jsonReader = new JsonReader("1.7976931348623157e308"); // 接近 double.MaxValue
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.IsInstanceOfType(jsonReader.Value, typeof(double));
- }
-
- [TestMethod]
- public void Read_StringWithUnescapedControlCharacter_ShouldThrowException()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"\u0001\""); // 未转义的控制字符
-
- // Act & Assert
- Assert.Throws(() => jsonReader.Read());
- }
-
- [TestMethod]
- public void Read_EmptyString_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("\"\"");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.String, jsonReader.TokenType);
- Assert.AreEqual("", jsonReader.Value);
- }
-
- [TestMethod]
- public void Read_Zero_ShouldReadCorrectly()
- {
- // Arrange
- using var jsonReader = new JsonReader("0");
-
- // Act
- Assert.IsTrue(jsonReader.Read());
-
- // Assert
- Assert.AreEqual(JsonTokenType.Number, jsonReader.TokenType);
- Assert.AreEqual(0L, jsonReader.Value);
- }
-}
\ No newline at end of file
diff --git a/tests/LuYao.Common.UnitTests/Text/Json/JsonWriterTests.cs b/tests/LuYao.Common.UnitTests/Text/Json/JsonWriterTests.cs
deleted file mode 100644
index 3569e6a..0000000
--- a/tests/LuYao.Common.UnitTests/Text/Json/JsonWriterTests.cs
+++ /dev/null
@@ -1,715 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.Globalization;
-using System.IO;
-using System.Text;
-
-namespace LuYao.Text.Json;
-
-[TestClass]
-public class JsonWriterTests
-{
- private StringBuilder _stringBuilder;
- private JsonWriter _writer;
-
- [TestInitialize]
- public void Setup()
- {
- _stringBuilder = new StringBuilder();
- _writer = new JsonWriter(_stringBuilder);
- }
-
- [TestCleanup]
- public void Cleanup()
- {
- _writer?.Dispose();
- }
-
- #region Constructor Tests
-
- [TestMethod]
- public void Constructor_WithTextWriter_ShouldCreateWriter()
- {
- // Arrange
- using var stringWriter = new StringWriter();
-
- // Act
- using var writer = new JsonWriter(stringWriter);
-
- // Assert
- Assert.IsNotNull(writer);
- }
-
- [TestMethod]
- public void Constructor_WithStringBuilder_ShouldCreateWriter()
- {
- // Arrange
- var sb = new StringBuilder();
-
- // Act
- using var writer = new JsonWriter(sb);
-
- // Assert
- Assert.IsNotNull(writer);
- }
-
- [TestMethod]
- public void Constructor_WithNullTextWriter_ShouldThrowArgumentNullException()
- {
- // Arrange & Act & Assert
- Assert.Throws(() => new JsonWriter((TextWriter)null));
- }
-
- [TestMethod]
- public void Constructor_WithIndentation_ShouldCreateIndentedWriter()
- {
- // Arrange
- var sb = new StringBuilder();
-
- // Act
- using var writer = new JsonWriter(sb, indented: true, indentString: " ");
-
- // Assert
- Assert.IsNotNull(writer);
- }
-
- #endregion
-
- #region Object Tests
-
- [TestMethod]
- public void WriteStartObject_ShouldWriteOpeningBrace()
- {
- // Act
- _writer.WriteStartObject();
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("{", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteEndObject_ShouldWriteClosingBrace()
- {
- // Act
- _writer.WriteStartObject();
- _writer.WriteEndObject();
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("{}", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteObject_WithIndentation_ShouldFormatCorrectly()
- {
- // Arrange
- var sb = new StringBuilder();
- using var writer = new JsonWriter(sb, indented: true);
-
- // Act
- writer.WriteStartObject();
- writer.WritePropertyName("test");
- writer.WriteValue("value");
- writer.WriteEndObject();
- writer.Flush();
-
- // Assert
- var result = sb.ToString();
- Assert.IsTrue(result.Contains("{\n"));
- Assert.IsTrue(result.Contains(" \"test\": \"value\"\n"));
- Assert.IsTrue(result.Contains("}"));
- }
-
- [TestMethod]
- public void WriteEndObject_WithoutStartObject_ShouldThrowInvalidOperationException()
- {
- // Act & Assert
- Assert.Throws(() => _writer.WriteEndObject());
- }
-
- #endregion
-
- #region Array Tests
-
- [TestMethod]
- public void WriteStartArray_ShouldWriteOpeningBracket()
- {
- // Act
- _writer.WriteStartArray();
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("[", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteEndArray_ShouldWriteClosingBracket()
- {
- // Act
- _writer.WriteStartArray();
- _writer.WriteEndArray();
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("[]", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteArray_WithIndentation_ShouldFormatCorrectly()
- {
- // Arrange
- var sb = new StringBuilder();
- using var writer = new JsonWriter(sb, indented: true);
-
- // Act
- writer.WriteStartArray();
- writer.WriteValue(1);
- writer.WriteValue(2);
- writer.WriteEndArray();
- writer.Flush();
-
- // Assert
- var result = sb.ToString();
- Assert.IsTrue(result.Contains("[\n"));
- Assert.IsTrue(result.Contains("1,\n"));
- Assert.IsTrue(result.Contains("2\n"));
- Assert.IsTrue(result.Contains("]"));
- }
-
- [TestMethod]
- public void WriteEndArray_WithoutStartArray_ShouldThrowInvalidOperationException()
- {
- // Act & Assert
- Assert.Throws(() => _writer.WriteEndArray());
- }
-
- #endregion
-
- #region Property Tests
-
- [TestMethod]
- public void WritePropertyName_ValidName_ShouldWriteQuotedName()
- {
- // Act
- _writer.WriteStartObject();
- _writer.WritePropertyName("test");
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("{\"test\":", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WritePropertyName_WithIndentation_ShouldIncludeSpace()
- {
- // Arrange
- var sb = new StringBuilder();
- using var writer = new JsonWriter(sb, indented: true);
-
- // Act
- writer.WriteStartObject();
- writer.WritePropertyName("test");
- writer.Flush();
-
- // Assert
- var result = sb.ToString();
- Assert.IsTrue(result.Contains("\"test\": "));
- }
-
- [TestMethod]
- public void WritePropertyName_NullName_ShouldThrowArgumentNullException()
- {
- // Act & Assert
- Assert.Throws(() => _writer.WritePropertyName(null));
- }
-
- #endregion
-
- #region String Value Tests
-
- [TestMethod]
- public void WriteValue_String_ShouldWriteQuotedString()
- {
- // Act
- _writer.WriteValue("test");
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("\"test\"", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_NullString_ShouldWriteNull()
- {
- // Act
- _writer.WriteValue((string)null);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("null", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_StringWithEscapeCharacters_ShouldEscapeCorrectly()
- {
- // Arrange
- var testCases = new[]
- {
- ("\"", "\\\""),
- ("\\", "\\\\"),
- ("\b", "\\b"),
- ("\f", "\\f"),
- ("\n", "\\n"),
- ("\r", "\\r"),
- ("\t", "\\t")
- };
-
- foreach (var (input, expected) in testCases)
- {
- // Arrange
- var sb = new StringBuilder();
- using var writer = new JsonWriter(sb);
-
- // Act
- writer.WriteValue(input);
- writer.Flush();
-
- // Assert
- Assert.AreEqual($"\"{expected}\"", sb.ToString(), $"Failed for input: {input}");
- }
- }
-
- [TestMethod]
- public void WriteValue_StringWithControlCharacters_ShouldUnicodeEscape()
- {
- // Act
- _writer.WriteValue("\u0001");
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("\"\\u0001\"", _stringBuilder.ToString());
- }
-
- #endregion
-
- #region Numeric Value Tests
-
- [TestMethod]
- public void WriteValue_Long_ShouldWriteNumber()
- {
- // Act
- _writer.WriteValue(12345L);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("12345", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_NegativeLong_ShouldWriteNegativeNumber()
- {
- // Act
- _writer.WriteValue(-12345L);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("-12345", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_Double_ShouldWriteNumber()
- {
- // Act
- _writer.WriteValue(123.45);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("123.45", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_DoubleNaN_ShouldWriteNull()
- {
- // Act
- _writer.WriteValue(double.NaN);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("null", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_DoubleInfinity_ShouldWriteNull()
- {
- // Act
- _writer.WriteValue(double.PositiveInfinity);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("null", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_DoubleNegativeInfinity_ShouldWriteNull()
- {
- // Act
- _writer.WriteValue(double.NegativeInfinity);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("null", _stringBuilder.ToString());
- }
-
- #endregion
-
- #region Boolean Value Tests
-
- [TestMethod]
- public void WriteValue_BooleanTrue_ShouldWriteTrue()
- {
- // Act
- _writer.WriteValue(true);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("true", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteValue_BooleanFalse_ShouldWriteFalse()
- {
- // Act
- _writer.WriteValue(false);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("false", _stringBuilder.ToString());
- }
-
- #endregion
-
- #region Null Value Tests
-
- [TestMethod]
- public void WriteNull_ShouldWriteNull()
- {
- // Act
- _writer.WriteNull();
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("null", _stringBuilder.ToString());
- }
-
- #endregion
-
- #region Raw Value Tests
-
- [TestMethod]
- public void WriteRaw_ValidJson_ShouldWriteRawValue()
- {
- // Act
- _writer.WriteRaw("{\"key\":\"value\"}");
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("{\"key\":\"value\"}", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteRaw_NullValue_ShouldThrowArgumentNullException()
- {
- // Act & Assert
- Assert.Throws(() => _writer.WriteRaw(null));
- }
-
- #endregion
-
- #region Complex JSON Tests
-
- [TestMethod]
- public void WriteComplexObject_ShouldProduceValidJson()
- {
- // Act
- _writer.WriteStartObject();
- _writer.WritePropertyName("name");
- _writer.WriteValue("John");
- _writer.WritePropertyName("age");
- _writer.WriteValue(30L);
- _writer.WritePropertyName("active");
- _writer.WriteValue(true);
- _writer.WritePropertyName("address");
- _writer.WriteStartObject();
- _writer.WritePropertyName("city");
- _writer.WriteValue("New York");
- _writer.WriteEndObject();
- _writer.WriteEndObject();
- _writer.Flush();
-
- // Assert
- var expected = "{\"name\":\"John\",\"age\":30,\"active\":true,\"address\":{\"city\":\"New York\"}}";
- Assert.AreEqual(expected, _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteComplexArray_ShouldProduceValidJson()
- {
- // Act
- _writer.WriteStartArray();
- _writer.WriteValue("item1");
- _writer.WriteValue(42L);
- _writer.WriteStartObject();
- _writer.WritePropertyName("nested");
- _writer.WriteValue("value");
- _writer.WriteEndObject();
- _writer.WriteEndArray();
- _writer.Flush();
-
- // Assert
- var expected = "[\"item1\",42,{\"nested\":\"value\"}]";
- Assert.AreEqual(expected, _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteNestedArraysAndObjects_ShouldProduceValidJson()
- {
- // Act
- _writer.WriteStartObject();
- _writer.WritePropertyName("data");
- _writer.WriteStartArray();
- _writer.WriteStartObject();
- _writer.WritePropertyName("id");
- _writer.WriteValue(1L);
- _writer.WritePropertyName("tags");
- _writer.WriteStartArray();
- _writer.WriteValue("tag1");
- _writer.WriteValue("tag2");
- _writer.WriteEndArray();
- _writer.WriteEndObject();
- _writer.WriteEndArray();
- _writer.WriteEndObject();
- _writer.Flush();
-
- // Assert
- var expected = "{\"data\":[{\"id\":1,\"tags\":[\"tag1\",\"tag2\"]}]}";
- Assert.AreEqual(expected, _stringBuilder.ToString());
- }
-
- #endregion
-
- #region Indentation Tests
-
- [TestMethod]
- public void WriteIndentedJson_ShouldFormatWithNewlinesAndIndentation()
- {
- // Arrange
- var sb = new StringBuilder();
- using var writer = new JsonWriter(sb, indented: true, indentString: " ");
-
- // Act
- writer.WriteStartObject();
- writer.WritePropertyName("name");
- writer.WriteValue("John");
- writer.WritePropertyName("items");
- writer.WriteStartArray();
- writer.WriteValue(1L);
- writer.WriteValue(2L);
- writer.WriteEndArray();
- writer.WriteEndObject();
- writer.Flush();
-
- // Assert
- var result = sb.ToString();
- Assert.IsTrue(result.Contains("{\n"));
- Assert.IsTrue(result.Contains(" \"name\": \"John\",\n"));
- Assert.IsTrue(result.Contains(" \"items\": [\n"));
- Assert.IsTrue(result.Contains(" 1,\n"));
- Assert.IsTrue(result.Contains(" 2\n"));
- Assert.IsTrue(result.Contains(" ]\n"));
- Assert.IsTrue(result.Contains("}"));
- }
-
- [TestMethod]
- public void WriteIndentedJson_CustomIndentString_ShouldUseCustomIndentation()
- {
- // Arrange
- var sb = new StringBuilder();
- using var writer = new JsonWriter(sb, indented: true, indentString: "\t");
-
- // Act
- writer.WriteStartObject();
- writer.WritePropertyName("key");
- writer.WriteValue("value");
- writer.WriteEndObject();
- writer.Flush();
-
- // Assert
- var result = sb.ToString();
- Assert.IsTrue(result.Contains("\t\"key\": \"value\""));
- }
-
- #endregion
-
- #region Buffer Management Tests
-
- [TestMethod]
- public void WriteValue_LongString_ShouldHandleBuffering()
- {
- // Arrange
- var longString = new string('a', 10000);
-
- // Act
- _writer.WriteValue(longString);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual($"\"{longString}\"", _stringBuilder.ToString());
- }
-
- [TestMethod]
- public void WriteMultipleValues_ShouldHandleBufferFlushes()
- {
- // Act
- for (int i = 0; i < 1000; i++)
- {
- if (i == 0)
- _writer.WriteStartArray();
-
- _writer.WriteValue($"item{i}");
-
- if (i == 999)
- _writer.WriteEndArray();
- }
- _writer.Flush();
-
- // Assert
- var result = _stringBuilder.ToString();
- Assert.IsTrue(result.StartsWith("["));
- Assert.IsTrue(result.EndsWith("]"));
- Assert.IsTrue(result.Contains("\"item0\""));
- Assert.IsTrue(result.Contains("\"item999\""));
- }
-
- #endregion
-
- #region Error Handling Tests
-
- [TestMethod]
- public void WriteInvalidStructure_ShouldThrowInvalidOperationException()
- {
- // Act & Assert
- _writer.WriteStartObject();
- Assert.Throws(() => _writer.WriteEndArray());
- }
-
- [TestMethod]
- public void WritePropertyNameOutsideObject_ShouldNotThrow()
- {
- // This test verifies current behavior - property names can be written at root level
- // Act
- _writer.WritePropertyName("test");
- _writer.WriteValue("value");
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("\"test\":\"value\"", _stringBuilder.ToString());
- }
-
- #endregion
-
- #region Disposal Tests
-
- [TestMethod]
- public void Dispose_ShouldFlushAndCleanup()
- {
- // Arrange
- var sb = new StringBuilder();
- var writer = new JsonWriter(sb);
-
- // Act
- writer.WriteValue("test");
- writer.Dispose();
-
- // Assert
- Assert.AreEqual("\"test\"", sb.ToString());
- }
-
- [TestMethod]
- public void Dispose_CalledTwice_ShouldNotThrow()
- {
- // Arrange
- var sb = new StringBuilder();
- var writer = new JsonWriter(sb);
-
- // Act & Assert
- writer.Dispose();
- writer.Dispose(); // Should not throw
- }
-
- [TestMethod]
- public void Dispose_WithOwnedWriter_ShouldDisposeUnderlyingWriter()
- {
- // Arrange
- var stringWriter = new StringWriter();
- var writer = new JsonWriter(stringWriter, ownsWriter: true);
-
- // Act
- writer.WriteValue("test");
- writer.Dispose();
-
- // Assert
- Assert.AreEqual("\"test\"", stringWriter.ToString());
- // Note: We can't easily verify that stringWriter is disposed without reflection
- }
-
- #endregion
-
- #region Culture Tests
-
- [TestMethod]
- public void WriteValue_DoubleWithDifferentCulture_ShouldUseInvariantCulture()
- {
- // Arrange
- var originalCulture = CultureInfo.CurrentCulture;
- try
- {
- CultureInfo.CurrentCulture = new CultureInfo("de-DE"); // Uses comma as decimal separator
-
- // Act
- _writer.WriteValue(123.45);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("123.45", _stringBuilder.ToString()); // Should use dot, not comma
- }
- finally
- {
- CultureInfo.CurrentCulture = originalCulture;
- }
- }
-
- [TestMethod]
- public void WriteValue_LongWithDifferentCulture_ShouldUseInvariantCulture()
- {
- // Arrange
- var originalCulture = CultureInfo.CurrentCulture;
- try
- {
- CultureInfo.CurrentCulture = new CultureInfo("en-IN"); // Uses different number formatting
-
- // Act
- _writer.WriteValue(1234567L);
- _writer.Flush();
-
- // Assert
- Assert.AreEqual("1234567", _stringBuilder.ToString()); // No thousand separators
- }
- finally
- {
- CultureInfo.CurrentCulture = originalCulture;
- }
- }
-
- #endregion
-}
\ No newline at end of file