Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<Compile Include="System\Text\Json\Nodes\JsonValue.cs" />
<Compile Include="System\Text\Json\Nodes\JsonValueOfTCustomized.cs" />
<Compile Include="System\Text\Json\Nodes\JsonValueOfT.cs" />
<Compile Include="System\Text\Json\Nodes\JsonValueOfJsonPrimitive.cs" />
<Compile Include="System\Text\Json\Nodes\JsonValueOfTPrimitive.cs" />
<Compile Include="System\Text\Json\Reader\ConsumeNumberResult.cs" />
<Compile Include="System\Text\Json\Reader\ConsumeTokenResult.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -694,29 +694,7 @@ internal bool TryGetValue(int index, out Guid value)
ReadOnlySpan<byte> data = _utf8Json.Span;
ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);

if (segment.Length > JsonConstants.MaximumEscapedGuidLength)
{
value = default;
return false;
}

// Segment needs to be unescaped
if (row.HasComplexChildren)
{
return JsonReaderHelper.TryGetEscapedGuid(segment, out value);
}

Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);

if (segment.Length == JsonConstants.MaximumFormatGuidLength
&& Utf8Parser.TryParse(segment, out Guid tmp, out _, 'D'))
{
value = tmp;
return true;
}

value = default;
return false;
return JsonReaderHelper.TryGetValue(segment, row.HasComplexChildren, out value);
}

internal string GetRawValueAsString(int index)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.Encodings.Web;

namespace System.Text.Json.Nodes
{
internal static class JsonValueOfJsonPrimitive
{
internal static JsonValue CreatePrimitiveValue(ref Utf8JsonReader reader, JsonNodeOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.False:
case JsonTokenType.True:
return new JsonValueOfJsonBool(reader.GetBoolean(), options);
case JsonTokenType.String:
byte[] buffer = new byte[reader.ValueLength];
ReadOnlyMemory<byte> utf8String = buffer.AsMemory(0, reader.CopyString(buffer));
return new JsonValueOfJsonString(utf8String, options);
case JsonTokenType.Number:
byte[] numberValue = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan.ToArray();
return new JsonValueOfJsonNumber(numberValue, options);
default:
Debug.Fail("Only primitives allowed.");
ThrowHelper.ThrowJsonException();
return null!; // Unreachable, but required for compilation.
}
}

private sealed class JsonValueOfJsonString : JsonValue
{
private readonly ReadOnlyMemory<byte> _value;

internal JsonValueOfJsonString(ReadOnlyMemory<byte> utf8String, JsonNodeOptions? options)
: base(options)
{
_value = utf8String;
}

internal override JsonNode DeepCloneCore() => new JsonValueOfJsonString(_value, Options);
private protected override JsonValueKind GetValueKindCore() => JsonValueKind.String;

public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null)
{
ArgumentNullException.ThrowIfNull(writer);

writer.WriteStringValue(_value.Span);
}

public override T GetValue<T>()
{
if (!TryGetValue(out T? value))
{
ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(JsonValueKind.String, typeof(T));
}

return value;
}

public override bool TryGetValue<T>([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;
}

if (typeof(T) == typeof(string))
{
string? result = JsonReaderHelper.TranscodeHelper(_value.Span);

Debug.Assert(result != null);
value = (T)(object)result;
return true;
}

bool 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(DateTimeOffset) || typeof(T) == typeof(DateTimeOffset?))
{
success = JsonReaderHelper.TryGetValue(_value.Span, isEscaped: false, out DateTimeOffset result);
value = (T)(object)result;
return 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(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;
}
}

value = default!;
return false;
}
}

private sealed class JsonValueOfJsonBool : JsonValue
{
private readonly bool _value;

private JsonValueKind ValueKind => _value ? JsonValueKind.True : JsonValueKind.False;

internal JsonValueOfJsonBool(bool value, JsonNodeOptions? options)
: base(options)
{
_value = value;
}

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;

public override T GetValue<T>()
{
if (!TryGetValue(out T? value))
{
ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(_value ? JsonValueKind.True : JsonValueKind.False, typeof(T));
}

return value;
}

public override bool TryGetValue<T>([NotNullWhen(true)] out T? value)
where T : default
{
if (typeof(T) == typeof(JsonElement))
{
value = (T)(object)JsonElement.Parse(_value ? JsonConstants.TrueValue : JsonConstants.FalseValue);
return true;
}

if (typeof(T) == typeof(bool) || typeof(T) == typeof(bool?))
{
value = (T)(object)_value;
return true;
}

value = default!;
return false;
}
}

private 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;
}

internal override JsonNode DeepCloneCore() => new JsonValueOfJsonNumber(_value, Options);
private protected override JsonValueKind GetValueKindCore() => JsonValueKind.Number;

public override T GetValue<T>()
{
if (!TryGetValue(out T? value))
{
ThrowHelper.ThrowInvalidOperationException_NodeUnableToConvertElement(JsonValueKind.Number, typeof(T));
}

return value;
}

public override bool TryGetValue<T>([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;

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;

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;

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;

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;

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;

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;

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;

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 = (T)(object)result;
return success;
}

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 = default!;
return false;
}

public override void WriteTo(Utf8JsonWriter writer, JsonSerializerOptions? options = null)
{
ArgumentNullException.ThrowIfNull(writer);

writer.WriteNumberValue(_value);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,33 @@ public static bool TryGetEscapedDateTimeOffset(ReadOnlySpan<byte> source, out Da
return false;
}

public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out Guid value)
{
if (segment.Length > JsonConstants.MaximumEscapedGuidLength)
{
value = default;
return false;
}

// Segment needs to be unescaped
if (isEscaped)
{
return TryGetEscapedGuid(segment, out value);
}

Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);

if (segment.Length == JsonConstants.MaximumFormatGuidLength
&& Utf8Parser.TryParse(segment, out Guid tmp, out _, 'D'))
{
value = tmp;
return true;
}

value = default;
return false;
}

public static bool TryGetEscapedGuid(ReadOnlySpan<byte> source, out Guid value)
{
Debug.Assert(source.Length <= JsonConstants.MaximumEscapedGuidLength);
Expand Down
Loading
Loading