diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs index fd86c0b19a31bc..97215bae51c85c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Threading; - namespace System.Text.Json.Nodes { public abstract partial class JsonNode @@ -44,14 +42,14 @@ public override string ToString() // Special case for string; don't quote it. if (this is JsonValue) { - if (this is JsonValuePrimitive jsonString) - { - return jsonString.Value; - } - - if (this is JsonValueOfElement { Value.ValueKind: JsonValueKind.String } jsonElement) + switch (this) { - return jsonElement.Value.GetString()!; + case JsonValuePrimitive jsonString: + return jsonString.Value; + case JsonValueOfElement { Value.ValueKind: JsonValueKind.String } jsonElement: + return jsonElement.Value.GetString()!; + case JsonValueOfJsonString jsonValueOfJsonString: + return jsonValueOfJsonString.GetValue()!; } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonValueOfJsonPrimitive.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonValueOfJsonPrimitive.cs index 379eafb8e13d90..78f1f1af55853f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonValueOfJsonPrimitive.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonValueOfJsonPrimitive.cs @@ -31,288 +31,288 @@ internal static JsonValue CreatePrimitiveValue(ref Utf8JsonReader reader, JsonNo return null!; // Unreachable, but required for compilation. } } + } + + internal sealed class JsonValueOfJsonString : JsonValue + { + private readonly ReadOnlyMemory _value; + + internal JsonValueOfJsonString(ReadOnlyMemory utf8String, JsonNodeOptions? options) + : base(options) + { + _value = utf8String; + } - private sealed class JsonValueOfJsonString : JsonValue + internal override JsonNode DeepCloneCore() => new JsonValueOfJsonString(_value, Options); + private protected override JsonValueKind GetValueKindCore() => JsonValueKind.String; + + public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null) { - private readonly ReadOnlyMemory _value; + ArgumentNullException.ThrowIfNull(writer); + + writer.WriteStringValue(_value.Span); + } - internal JsonValueOfJsonString(ReadOnlyMemory utf8String, JsonNodeOptions? options) - : base(options) + public override T GetValue() + { + if (!TryGetValue(out T? value)) { - _value = utf8String; + ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(JsonValueKind.String, typeof(T)); } - internal override JsonNode DeepCloneCore() => new JsonValueOfJsonString(_value, Options); - private protected override JsonValueKind GetValueKindCore() => JsonValueKind.String; + return value; + } - public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null) + public override bool TryGetValue([NotNullWhen(true)] out T? value) + where T : default + { + if (typeof(T) == typeof(JsonElement)) { - ArgumentNullException.ThrowIfNull(writer); - - writer.WriteStringValue(_value.Span); + value = (T)(object)JsonWriterHelper.WriteString(_value.Span, static serialized => JsonElement.Parse(serialized)); + return true; } - public override T GetValue() + if (typeof(T) == typeof(string)) { - if (!TryGetValue(out T? value)) - { - ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(JsonValueKind.String, typeof(T)); - } + string? result = JsonReaderHelper.TranscodeHelper(_value.Span); - return value; + Debug.Assert(result != null); + value = (T)(object)result; + return true; } - public override bool TryGetValue([NotNullWhen(true)] out T? value) - where T : default - { - if (typeof(T) == typeof(JsonElement)) - { - value = (T)(object)JsonWriterHelper.WriteString(_value.Span, static serialized => JsonElement.Parse(serialized)); - return true; - } + bool success; - if (typeof(T) == typeof(string)) - { - string? result = JsonReaderHelper.TranscodeHelper(_value.Span); + if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?)) + { + success = JsonReaderHelper.TryGetValue(_value.Span, isEscaped: false, out DateTime result); + value = (T)(object)result; + return success; + } - Debug.Assert(result != null); - value = (T)(object)result; - return true; - } + if (typeof(T) == typeof(DateTimeOffset) || typeof(T) == typeof(DateTimeOffset?)) + { + success = JsonReaderHelper.TryGetValue(_value.Span, isEscaped: false, out DateTimeOffset result); + value = (T)(object)result; + return success; + } - bool success; + if (typeof(T) == typeof(Guid) || typeof(T) == typeof(Guid?)) + { + success = JsonReaderHelper.TryGetValue(_value.Span, isEscaped: false, out Guid result); + value = (T)(object)result; + return success; + } - if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?)) - { - success = JsonReaderHelper.TryGetValue(_value.Span, isEscaped: false, out DateTime result); - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(char) || typeof(T) == typeof(char?)) + { + string? result = JsonReaderHelper.TranscodeHelper(_value.Span); - if (typeof(T) == typeof(DateTimeOffset) || typeof(T) == typeof(DateTimeOffset?)) + Debug.Assert(result != null); + if (result.Length == 1) { - success = JsonReaderHelper.TryGetValue(_value.Span, isEscaped: false, out DateTimeOffset result); - value = (T)(object)result; - return success; + value = (T)(object)result[0]; + return true; } + } - if (typeof(T) == typeof(Guid) || typeof(T) == typeof(Guid?)) - { - success = JsonReaderHelper.TryGetValue(_value.Span, isEscaped: false, out Guid result); - value = (T)(object)result; - return success; - } + value = default!; + return false; + } + } - if (typeof(T) == typeof(char) || typeof(T) == typeof(char?)) - { - string? result = JsonReaderHelper.TranscodeHelper(_value.Span); - - Debug.Assert(result != null); - if (result.Length == 1) - { - value = (T)(object)result[0]; - return true; - } - } + internal sealed class JsonValueOfJsonBool : JsonValue + { + private readonly bool _value; - value = default!; - return false; - } - } + private JsonValueKind ValueKind => _value ? JsonValueKind.True : JsonValueKind.False; - private sealed class JsonValueOfJsonBool : JsonValue + internal JsonValueOfJsonBool(bool value, JsonNodeOptions? options) + : base(options) { - private readonly bool _value; + _value = value; + } - private JsonValueKind ValueKind => _value ? JsonValueKind.True : JsonValueKind.False; + public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null) => writer.WriteBooleanValue(_value); + internal override JsonNode DeepCloneCore() => new JsonValueOfJsonBool(_value, Options); + private protected override JsonValueKind GetValueKindCore() => ValueKind; - internal JsonValueOfJsonBool(bool value, JsonNodeOptions? options) - : base(options) + public override T GetValue() + { + if (!TryGetValue(out T? value)) { - _value = value; + ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(_value ? JsonValueKind.True : JsonValueKind.False, typeof(T)); } - public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null) => writer.WriteBooleanValue(_value); - internal override JsonNode DeepCloneCore() => new JsonValueOfJsonBool(_value, Options); - private protected override JsonValueKind GetValueKindCore() => ValueKind; + return value; + } - public override T GetValue() + public override bool TryGetValue([NotNullWhen(true)] out T? value) + where T : default + { + if (typeof(T) == typeof(JsonElement)) { - if (!TryGetValue(out T? value)) - { - ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(_value ? JsonValueKind.True : JsonValueKind.False, typeof(T)); - } - - return value; + value = (T)(object)JsonElement.Parse(_value ? JsonConstants.TrueValue : JsonConstants.FalseValue); + return true; } - public override bool TryGetValue([NotNullWhen(true)] out T? value) - where T : default + if (typeof(T) == typeof(bool) || typeof(T) == typeof(bool?)) { - if (typeof(T) == typeof(JsonElement)) - { - value = (T)(object)JsonElement.Parse(_value ? JsonConstants.TrueValue : JsonConstants.FalseValue); - return true; - } + value = (T)(object)_value; + return true; + } - if (typeof(T) == typeof(bool) || typeof(T) == typeof(bool?)) - { - value = (T)(object)_value; - return true; - } + value = default!; + return false; + } + } - value = default!; - return false; - } + internal sealed class JsonValueOfJsonNumber : JsonValue + { + // This can be optimized to store the decimal point position and the exponent so that + // conversion to different numeric types can be done without parsing the string again. + // Utf8Parser uses an internal ref struct, Number.NumberBuffer, which is really the + // same functionality that we would want here. + private readonly byte[] _value; + + internal JsonValueOfJsonNumber(byte[] number, JsonNodeOptions? options) + : base(options) + { + _value = number; } - private sealed class JsonValueOfJsonNumber : JsonValue + internal override JsonNode DeepCloneCore() => new JsonValueOfJsonNumber(_value, Options); + private protected override JsonValueKind GetValueKindCore() => JsonValueKind.Number; + + public override T GetValue() { - // This can be optimized to store the decimal point position and the exponent so that - // conversion to different numeric types can be done without parsing the string again. - // Utf8Parser uses an internal ref struct, Number.NumberBuffer, which is really the - // same functionality that we would want here. - private readonly byte[] _value; - - internal JsonValueOfJsonNumber(byte[] number, JsonNodeOptions? options) - : base(options) + if (!TryGetValue(out T? value)) { - _value = number; + ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(JsonValueKind.Number, typeof(T)); } - internal override JsonNode DeepCloneCore() => new JsonValueOfJsonNumber(_value, Options); - private protected override JsonValueKind GetValueKindCore() => JsonValueKind.Number; + return value; + } - public override T GetValue() + public override bool TryGetValue([NotNullWhen(true)] out T? value) + where T : default + { + if (typeof(T) == typeof(JsonElement)) { - if (!TryGetValue(out T? value)) - { - ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(JsonValueKind.Number, typeof(T)); - } - - return value; + value = (T)(object)JsonElement.Parse(_value); + return true; } - public override bool TryGetValue([NotNullWhen(true)] out T? value) - where T : default - { - if (typeof(T) == typeof(JsonElement)) - { - value = (T)(object)JsonElement.Parse(_value); - return true; - } - - bool success; - - if (typeof(T) == typeof(int) || typeof(T) == typeof(int?)) - { - success = Utf8Parser.TryParse(_value, out int result, out int consumed) && - consumed == _value.Length; - - value = (T)(object)result; - return success; - } - - if (typeof(T) == typeof(long) || typeof(T) == typeof(long?)) - { - success = Utf8Parser.TryParse(_value, out long result, out int consumed) && - consumed == _value.Length; + bool success; - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(int) || typeof(T) == typeof(int?)) + { + success = Utf8Parser.TryParse(_value, out int result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(double) || typeof(T) == typeof(double?)) - { - success = Utf8Parser.TryParse(_value, out double result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(long) || typeof(T) == typeof(long?)) + { + success = Utf8Parser.TryParse(_value, out long result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(short) || typeof(T) == typeof(short?)) - { - success = Utf8Parser.TryParse(_value, out short result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(double) || typeof(T) == typeof(double?)) + { + success = Utf8Parser.TryParse(_value, out double result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?)) - { - success = Utf8Parser.TryParse(_value, out decimal result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(short) || typeof(T) == typeof(short?)) + { + success = Utf8Parser.TryParse(_value, out short result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?)) - { - success = Utf8Parser.TryParse(_value, out byte result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?)) + { + success = Utf8Parser.TryParse(_value, out decimal result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(float) || typeof(T) == typeof(float?)) - { - success = Utf8Parser.TryParse(_value, out float result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?)) + { + success = Utf8Parser.TryParse(_value, out byte result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?)) - { - success = Utf8Parser.TryParse(_value, out uint result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(float) || typeof(T) == typeof(float?)) + { + success = Utf8Parser.TryParse(_value, out float result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?)) - { - success = Utf8Parser.TryParse(_value, out ushort result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?)) + { + success = Utf8Parser.TryParse(_value, out uint result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?)) - { - success = Utf8Parser.TryParse(_value, out ulong result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?)) + { + success = Utf8Parser.TryParse(_value, out ushort result, out int consumed) && + consumed == _value.Length; - if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?)) - { - success = Utf8Parser.TryParse(_value, out sbyte result, out int consumed) && - consumed == _value.Length; + value = (T)(object)result; + return success; + } - value = (T)(object)result; - return success; - } + if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?)) + { + success = Utf8Parser.TryParse(_value, out ulong result, out int consumed) && + consumed == _value.Length; - value = default!; - return false; + value = (T)(object)result; + return success; } - public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null) + if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?)) { - ArgumentNullException.ThrowIfNull(writer); + success = Utf8Parser.TryParse(_value, out sbyte result, out int consumed) && + consumed == _value.Length; - writer.WriteNumberValue(_value); + value = (T)(object)result; + return success; } + + value = default!; + return false; + } + + public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null) + { + ArgumentNullException.ThrowIfNull(writer); + + writer.WriteNumberValue(_value); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ToStringTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ToStringTests.cs index 9fd937fadddcc4..eed98a1da6ef02 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ToStringTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/ToStringTests.cs @@ -26,6 +26,10 @@ public static void NodeToString_StringValuesNotQuoted() node = JsonValue.Create("Hello"); json = node.ToString(); Assert.Equal("Hello", json); + + node = JsonSerializer.Deserialize("\"Hello\""); + json = node.ToString(); + Assert.Equal("Hello", json); } } }