Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add interop between serializer and DOMs #56112

Merged
merged 4 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
36 changes: 36 additions & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ public static partial class JsonSerializer
public static object? Deserialize(string json, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static object? Deserialize(string json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static object? Deserialize(this System.Text.Json.JsonDocument document, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static object? Deserialize(this System.Text.Json.JsonDocument document, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static object? Deserialize(this System.Text.Json.JsonElement element, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static object? Deserialize(this System.Text.Json.JsonElement element, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static object? Deserialize(this System.Text.Json.Nodes.JsonNode? node, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static object? Deserialize(this System.Text.Json.Nodes.JsonNode? node, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static object? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static object? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
Expand All @@ -222,6 +231,15 @@ public static partial class JsonSerializer
public static TValue? Deserialize<TValue>(string json, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static TValue? Deserialize<TValue>(string json, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static TValue? Deserialize<TValue>(this System.Text.Json.JsonDocument document, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static TValue? Deserialize<TValue>(this System.Text.Json.JsonDocument document, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static TValue? Deserialize<TValue>(this System.Text.Json.JsonElement element, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static TValue? Deserialize<TValue>(this System.Text.Json.JsonElement element, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static TValue? Deserialize<TValue>(this System.Text.Json.Nodes.JsonNode? node, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static TValue? Deserialize<TValue>(this System.Text.Json.Nodes.JsonNode? node, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static TValue? Deserialize< TValue>(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static TValue? Deserialize<TValue>(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
Expand All @@ -240,6 +258,24 @@ public static void Serialize(System.Text.Json.Utf8JsonWriter writer, object? val
public static System.Threading.Tasks.Task SerializeAsync<TValue>(System.IO.Stream utf8Json, TValue value, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task SerializeAsync<TValue>(System.IO.Stream utf8Json, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static System.Text.Json.JsonDocument SerializeToDocument(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the SerializetTo* methods also come in async variants?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we could have added async; it wasn't part of the ask. The approved list of API permutations were already greater than originally expected.

Today, JsonDocument only has ParseAsync() (no serialize behavior) so the workaround for that is something like:

    Stream stream = ...

    // Serialize object to stream
    await JsonSerializer.SerializeAsync(stream, myObject);

    // Deserialize stream to document
    JsonDocument doc = await JsonDocument.ParseAsync(stream);

public static System.Text.Json.JsonDocument SerializeToDocument(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static System.Text.Json.JsonDocument SerializeToDocument<TValue>(TValue value, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static System.Text.Json.JsonDocument SerializeToDocument<TValue>(TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static System.Text.Json.JsonElement SerializeToElement(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static System.Text.Json.JsonElement SerializeToElement(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static System.Text.Json.JsonElement SerializeToElement<TValue>(TValue value, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static System.Text.Json.JsonElement SerializeToElement<TValue>(TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static System.Text.Json.Nodes.JsonNode? SerializeToNode(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static System.Text.Json.Nodes.JsonNode? SerializeToNode(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static System.Text.Json.Nodes.JsonNode? SerializeToNode<TValue>(TValue value, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static System.Text.Json.Nodes.JsonNode? SerializeToNode<TValue>(TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue> jsonTypeInfo) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
public static byte[] SerializeToUtf8Bytes(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; }
public static byte[] SerializeToUtf8Bytes(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
Expand Down
6 changes: 6 additions & 0 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@
<Compile Include="System\Text\Json\Serialization\IJsonOnDeserializing.cs" />
<Compile Include="System\Text\Json\Serialization\IJsonOnSerialized.cs" />
<Compile Include="System\Text\Json\Serialization\IJsonOnSerializing.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.Document.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.Element.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.Node.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Document.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Element.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Node.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializerContext.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\FSharpCoreReflectionProxy.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonMetadataServices.Collections.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public sealed partial class JsonDocument
/// </exception>
public static JsonDocument Parse(ReadOnlyMemory<byte> utf8Json, JsonDocumentOptions options = default)
{
return Parse(utf8Json, options.GetReaderOptions(), null);
return Parse(utf8Json, options.GetReaderOptions());
}

/// <summary>
Expand Down Expand Up @@ -80,7 +80,7 @@ public static JsonDocument Parse(ReadOnlySequence<byte> utf8Json, JsonDocumentOp

if (utf8Json.IsSingleSegment)
{
return Parse(utf8Json.First, readerOptions, null);
return Parse(utf8Json.First, readerOptions);
}

int length = checked((int)utf8Json.Length);
Expand Down Expand Up @@ -137,6 +137,15 @@ public static JsonDocument Parse(Stream utf8Json, JsonDocumentOptions options =
}
}

internal static JsonDocument Parse(PooledByteBufferWriter utf8Json, JsonDocumentOptions options = default)
{
return Parse(
utf8Json.WrittenMemory,
options.GetReaderOptions(),
extraRentedArrayPoolBytes: null,
extraPooledByteBufferWriter: utf8Json);
}

internal static JsonDocument ParseValue(Stream utf8Json, JsonDocumentOptions options)
{
Debug.Assert(utf8Json != null);
Expand Down Expand Up @@ -664,14 +673,15 @@ JsonDocument Create(byte[] utf8Json)
{
MetadataDb database = MetadataDb.CreateLocked(utf8Json.Length);
database.Append(tokenType, startLocation: 0, utf8Json.Length);
return new JsonDocument(utf8Json, database, extraRentedBytes: null);
return new JsonDocument(utf8Json, database);
}
}

private static JsonDocument Parse(
ReadOnlyMemory<byte> utf8Json,
JsonReaderOptions readerOptions,
byte[]? extraRentedBytes)
byte[]? extraRentedArrayPoolBytes = null,
PooledByteBufferWriter? extraPooledByteBufferWriter = null)
{
ReadOnlySpan<byte> utf8JsonSpan = utf8Json.Span;
var database = MetadataDb.CreateRented(utf8Json.Length, convertToAlloc: false);
Expand All @@ -691,7 +701,7 @@ private static JsonDocument Parse(
stack.Dispose();
}

return new JsonDocument(utf8Json, database, extraRentedBytes);
return new JsonDocument(utf8Json, database, extraRentedArrayPoolBytes, extraPooledByteBufferWriter);
}

private static JsonDocument ParseUnrented(
Expand Down Expand Up @@ -729,7 +739,7 @@ private static JsonDocument ParseUnrented(
}
}

return new JsonDocument(utf8Json, database, extraRentedBytes: null);
return new JsonDocument(utf8Json, database);
}

private static ArraySegment<byte> ReadToEnd(Stream stream)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ public sealed partial class JsonDocument : IDisposable
{
private ReadOnlyMemory<byte> _utf8Json;
private MetadataDb _parsedData;
private byte[]? _extraRentedBytes;

private byte[]? _extraRentedArrayPoolBytes;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are the extra flags needed? They seem to be equivalent to checking whether the rented references are null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing pattern was thread-safe using Interlocked.Exchange(). Instead of doing two calls to Interlocked.Exchange() (the original one and the new one), a bool was added that will avoid the Interlocked.Exchange() if not necessary.

private bool _hasExtraRentedArrayPoolBytes;

private PooledByteBufferWriter? _extraPooledByteBufferWriter;
private bool _hasExtraPooledByteBufferWriter;

private (int, string?) _lastIndexAndString = (-1, null);

internal bool IsDisposable { get; }
Expand All @@ -36,19 +42,34 @@ public sealed partial class JsonDocument : IDisposable
private JsonDocument(
ReadOnlyMemory<byte> utf8Json,
MetadataDb parsedData,
byte[]? extraRentedBytes,
byte[]? extraRentedArrayPoolBytes = null,
PooledByteBufferWriter? extraPooledByteBufferWriter = null,
bool isDisposable = true)
{
Debug.Assert(!utf8Json.IsEmpty);

// We never have both rented fields.
Debug.Assert(extraRentedArrayPoolBytes == null || extraPooledByteBufferWriter == null);

_utf8Json = utf8Json;
_parsedData = parsedData;
_extraRentedBytes = extraRentedBytes;

if (_extraRentedArrayPoolBytes != null)
{
_hasExtraRentedArrayPoolBytes = true;
_extraRentedArrayPoolBytes = extraRentedArrayPoolBytes;
}
else if (extraPooledByteBufferWriter != null)
{
_hasExtraPooledByteBufferWriter = true;
_extraPooledByteBufferWriter = extraPooledByteBufferWriter;
}


IsDisposable = isDisposable;

// extraRentedBytes better be null if we're not disposable.
Debug.Assert(isDisposable || extraRentedBytes == null);
// Both rented fields better be null if we're not disposable.
Debug.Assert(isDisposable || (_extraRentedArrayPoolBytes == null && _extraPooledByteBufferWriter == null));
}

/// <inheritdoc />
Expand All @@ -65,12 +86,20 @@ public void Dispose()

// When "extra rented bytes exist" they contain the document,
// and thus need to be cleared before being returned.
byte[]? extraRentedBytes = Interlocked.Exchange(ref _extraRentedBytes, null);
if (_hasExtraRentedArrayPoolBytes)
{
byte[]? extraRentedBytes = Interlocked.Exchange(ref _extraRentedArrayPoolBytes, null);

if (extraRentedBytes != null)
if (extraRentedBytes != null)
{
extraRentedBytes.AsSpan(0, length).Clear();
ArrayPool<byte>.Shared.Return(extraRentedBytes);
}
}
else if (_hasExtraPooledByteBufferWriter)
{
extraRentedBytes.AsSpan(0, length).Clear();
ArrayPool<byte>.Shared.Return(extraRentedBytes);
PooledByteBufferWriter? extraBufferWriter = Interlocked.Exchange(ref _extraPooledByteBufferWriter, null);
extraBufferWriter?.Dispose();
}
}

Expand Down Expand Up @@ -184,7 +213,7 @@ internal int GetEndIndex(int index, bool includeEndElement)
return endIndex;
}

private ReadOnlyMemory<byte> GetRawValue(int index, bool includeQuotes)
internal ReadOnlyMemory<byte> GetRawValue(int index, bool includeQuotes)
{
CheckNotDisposed();

Expand Down Expand Up @@ -764,7 +793,12 @@ internal JsonElement CloneElement(int index)
ReadOnlyMemory<byte> segmentCopy = GetRawValue(index, includeQuotes: true).ToArray();

JsonDocument newDocument =
new JsonDocument(segmentCopy, newDb, extraRentedBytes: null, isDisposable: false);
new JsonDocument(
segmentCopy,
newDb,
extraRentedArrayPoolBytes: null,
extraPooledByteBufferWriter: null,
isDisposable: false);

return newDocument.RootElement;
}
Expand Down
Loading