Skip to content

Commit

Permalink
Performance improvements in JsonValue.
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis committed Jun 19, 2024
1 parent c6779ba commit 124e54c
Show file tree
Hide file tree
Showing 13 changed files with 452 additions and 405 deletions.
7 changes: 1 addition & 6 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<Compile Include="System\Text\Json\JsonTokenType.cs" />
<Compile Include="System\Text\Json\Nodes\JsonArray.cs" />
<Compile Include="System\Text\Json\Nodes\JsonArray.IList.cs" />
<Compile Include="System\Text\Json\Nodes\JsonValueOfElement.cs" />
<Compile Include="System\Text\Json\Nodes\JsonNode.cs" />
<Compile Include="System\Text\Json\Nodes\JsonNode.Operators.cs" />
<Compile Include="System\Text\Json\Nodes\JsonNode.Parse.cs" />
Expand Down Expand Up @@ -380,12 +381,6 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<Compile Include="System\Text\Json\Reader\JsonReaderHelper.netstandard.cs" />
</ItemGroup>

<ItemGroup>
<None Include="System\Text\Json\OrderedDictionary.cs" />
<None Include="System\Text\Json\OrderedDictionary.KeyCollection.cs" />
<None Include="System\Text\Json\OrderedDictionary.ValueCollection.cs" />
</ItemGroup>

<!-- Application tfms (.NETCoreApp, .NETFramework) need to use the same or higher version of .NETStandard's dependencies. -->
<ItemGroup>
<ProjectReference Include="$(LibrariesProjectRoot)System.Text.Encodings.Web\src\System.Text.Encodings.Web.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ internal JsonTokenType GetJsonTokenType(int index)
return _parsedData.GetJsonTokenType(index);
}

internal bool GetHasComplexChildren(int index)
{
CheckNotDisposed();
DbRow row = _parsedData.Get(index);
return row.HasComplexChildren;
}

internal int GetArrayLength(int index)
{
CheckNotDisposed();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,28 @@ internal string GetPropertyRawText()
return _parent.GetPropertyRawValueAsString(_idx);
}

internal bool ValueIsEscaped
{
// TODO make public https://github.com/dotnet/runtime/issues/77666
get
{
Debug.Assert(ValueKind is JsonValueKind.String);
CheckValidInstance();
return _parent.GetHasComplexChildren(_idx);
}
}

internal ReadOnlySpan<byte> ValueSpan
{
// TODO make public https://github.com/dotnet/runtime/issues/77666
get
{
Debug.Assert(ValueKind is JsonValueKind.String);
CheckValidInstance();
return _parent.GetRawValue(_idx, includeQuotes: false).Span;
}
}

/// <summary>
/// Compares <paramref name="text" /> to the string value of this element.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ namespace System.Text.Json.Nodes
/// declared as an <see cref="object"/> should be deserialized as a <see cref="JsonNode"/>.
public abstract partial class JsonNode
{
// Default options instance used when calling built-in JsonNode converters.
private protected static readonly JsonSerializerOptions s_defaultOptions = new();

private JsonNode? _parent;
private JsonNodeOptions? _options;

Expand Down Expand Up @@ -375,7 +378,7 @@ internal void AssignParent(JsonNode parent)
}

var jsonTypeInfo = (JsonTypeInfo<T>)JsonSerializerOptions.Default.GetTypeInfo(typeof(T));
return new JsonValueCustomized<T>(value, jsonTypeInfo, options);
return JsonValue.CreateFromTypeInfo(value, jsonTypeInfo, options);
}
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,18 @@ private protected JsonValue(JsonNodeOptions? options = null) : base(options) { }
return null;
}

if (value is JsonElement element)
if (value is JsonNode)
{
if (element.ValueKind is JsonValueKind.Null)
{
return null;
}

VerifyJsonElementIsNotArrayOrObject(ref element);
ThrowHelper.ThrowArgumentException_NodeValueNotAllowed(nameof(value));
}

return new JsonValuePrimitive<JsonElement>(element, JsonMetadataServices.JsonElementConverter, options);
if (value is JsonElement element)
{
return CreateFromElement(ref element, options);
}

var jsonTypeInfo = (JsonTypeInfo<T>)JsonSerializerOptions.Default.GetTypeInfo(typeof(T));
return new JsonValueCustomized<T>(value, jsonTypeInfo, options);
return CreateFromTypeInfo(value, jsonTypeInfo, options);
}

/// <summary>
Expand All @@ -76,21 +74,22 @@ private protected JsonValue(JsonNodeOptions? options = null) : base(options) { }
return null;
}

if (value is JsonElement element)
if (value is JsonNode)
{
if (element.ValueKind is JsonValueKind.Null)
{
return null;
}

VerifyJsonElementIsNotArrayOrObject(ref element);
ThrowHelper.ThrowArgumentException_NodeValueNotAllowed(nameof(value));
}

jsonTypeInfo.EnsureConfigured();
return new JsonValueCustomized<T>(value, jsonTypeInfo, options);

if (value is JsonElement element && jsonTypeInfo.EffectiveConverter.IsInternalConverter)
{
return CreateFromElement(ref element, options);
}

return CreateFromTypeInfo(value, jsonTypeInfo, options);
}

internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
internal sealed override void GetPath(ref ValueStringBuilder path, JsonNode? child)
{
Debug.Assert(child == null);

Expand All @@ -114,13 +113,35 @@ internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
/// <returns><see langword="true"/> if the value can be successfully obtained; otherwise, <see langword="false"/>.</returns>
public abstract bool TryGetValue<T>([NotNullWhen(true)] out T? value);

private static void VerifyJsonElementIsNotArrayOrObject(ref JsonElement element)
internal static JsonValue CreateFromTypeInfo<T>(T value, JsonTypeInfo<T> jsonTypeInfo, JsonNodeOptions? options = null)
{
Debug.Assert(jsonTypeInfo.IsConfigured);
Debug.Assert(value != null);

if (jsonTypeInfo.EffectiveConverter.IsInternalConverter && JsonValue<T>.TypeIsSupportedPrimitive)
{
// If the type is using the built-in converter for a known primitive,
// switch to the more efficient JsonValuePrimitive<T> implementation.
return new JsonValuePrimitive<T>(value, jsonTypeInfo.EffectiveConverter, options);
}

return new JsonValueCustomized<T>(value, jsonTypeInfo, options);
}

internal static JsonValue? CreateFromElement(ref readonly JsonElement element, JsonNodeOptions? options = null)
{
if (element.ValueKind is JsonValueKind.Null)
{
return null;
}

// Force usage of JsonArray and JsonObject instead of supporting those in an JsonValue.
if (element.ValueKind is JsonValueKind.Object or JsonValueKind.Array)
{
ThrowHelper.ThrowInvalidOperationException_NodeElementCannotBeObjectOrArray();
}

return new JsonValueOfElement(element, options);
}
}
}
Loading

0 comments on commit 124e54c

Please sign in to comment.