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

Conversation

steveharter
Copy link
Member

resolves #31274

Verified 100% code coverage and increased coverage for public API validation in other areas including JsonSerializer APIs that use Stream.

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Jul 21, 2021

Tagging subscribers to this area: @eiriktsarpalis, @layomia
See info in area-owners.md if you want to be subscribed.

Issue Details

resolves #31274

Verified 100% code coverage and increased coverage for public API validation in other areas including JsonSerializer APIs that use Stream.

Author: steveharter
Assignees: steveharter
Labels:

area-System.Text.Json

Milestone: 6.0.0

@@ -240,6 +258,24 @@ public static partial class JsonSerializer
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);

@@ -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.


// For performance, we avoid calling JsonSerializer.Deserialize(node) since that will allocate an extra byte[].

using (var output = new PooledByteBufferWriter(options.DefaultBufferSize))
Copy link
Member

Choose a reason for hiding this comment

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

Nit: could avoid two layers of indentation with using var syntax.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did clean up the PooledByteBufferWriter, but left the writer's using since it isn't used all the way to the end of the method and I prefer the variable scoping.

}
else
{
node.WriteTo(writer, options);
Copy link
Member

Choose a reason for hiding this comment

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

I wish there was a way we could avoid writing to an intermediate buffer. At this point it's probably not possible with Utf8JsonReader being a concrete ref struct.

Copy link
Member Author

@steveharter steveharter Jul 26, 2021

Choose a reason for hiding this comment

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

At least we only have one intermediate buffer; the current workarounds (before this new support) would have 2.

In theory, we could add another type of reader (and writer; possibly derived) to avoid this.

Copy link
Contributor

@layomia layomia left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -28,6 +28,7 @@ public override void Write(Utf8JsonWriter writer, JsonNode? value, JsonSerialize
if (value == null)
{
writer.WriteNullValue();
// Note the JsonSerializer.Deserialize<T>(JsonNode?) also calls WriteNullValue() for a null + root JsonNode.
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have the right serializer method referenced in this comment?

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 -- the Deserialize() for JsonNode has a special check for null to allow it` to be properly deserialized (since a node is actually nullable, unlike document\element).


private static TValue? ReadUsingMetadata<TValue>(JsonDocument document, JsonTypeInfo jsonTypeInfo)
{
ReadOnlySpan<byte> utf8Json = document.GetRawValue(0, includeQuotes: true).Span;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this implementation have called document.RootElement and passed that to ReadUsingMetadata<TValue>(JsonElement element, JsonTypeInfo jsonTypeInfo)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. Thanks

@steveharter steveharter merged commit ddc729f into dotnet:main Jul 28, 2021
@steveharter steveharter deleted the DomExtensions branch July 28, 2021 01:35
thaystg added a commit to thaystg/runtime that referenced this pull request Jul 28, 2021
…bug_tests

* origin/main: (274 commits)
  Disable test ConnectWithCertificateForDifferentName_Throws (dotnet#56456)
  Update dependencies from https://github.com/mono/linker build 20210726.2 (dotnet#56374)
  Cleanup disabled test conditions (dotnet#56381)
  [mono] Add GC unsafe transition to mono_unhandled_exception  (dotnet#56380)
  don't fail the file extraction when we can't set last write time (dotnet#56370)
  Properly rebuild optimization data when it changes (dotnet#56397)
  Make open function calls in coreclr EINTR resilient on macOS (dotnet#56403)
  Fix dependency from EventLog to TraceSource ref (dotnet#56417)
  Fix comments in asm with JitDiffableDasm=1 (dotnet#56416)
  Catch TcpClient ctor exceptions in FtpWebRequest.CreateConnectionAsync (dotnet#56379)
  Add interop between serializer and DOMs (dotnet#56112)
  Fix type loader not recognizing overridden method (dotnet#56337)
  Prevent Segfault in EventPipe on disable (dotnet#56104)
  Update runtimeconfig.json and deps.json paths when these break past the MAX_PATH threshold  (dotnet#56224)
  Use native allocator instead of pinning when decompressing embedded PDB (dotnet#56336)
  Specify win-x64 as a valid platform in the microsoft-net-runtime-* workloads for iOS/tvOS/MacCatalyst (dotnet#56311)
  Fix FailFast message formatting race (dotnet#56388)
  Try to fix finalizer-based async tests (dotnet#56384)
  Fix MetricsEventSource tests (dotnet#56382)
  Remove invalid Castle.DynamicProxy.Internal.AbstractInvocation from ILLink descriptor files (dotnet#56392)
  ...
thaystg added a commit to thaystg/runtime that referenced this pull request Jul 28, 2021
* origin/main: (95 commits)
  Disable test ConnectWithCertificateForDifferentName_Throws (dotnet#56456)
  Update dependencies from https://github.com/mono/linker build 20210726.2 (dotnet#56374)
  Cleanup disabled test conditions (dotnet#56381)
  [mono] Add GC unsafe transition to mono_unhandled_exception  (dotnet#56380)
  don't fail the file extraction when we can't set last write time (dotnet#56370)
  Properly rebuild optimization data when it changes (dotnet#56397)
  Make open function calls in coreclr EINTR resilient on macOS (dotnet#56403)
  Fix dependency from EventLog to TraceSource ref (dotnet#56417)
  Fix comments in asm with JitDiffableDasm=1 (dotnet#56416)
  Catch TcpClient ctor exceptions in FtpWebRequest.CreateConnectionAsync (dotnet#56379)
  Add interop between serializer and DOMs (dotnet#56112)
  Fix type loader not recognizing overridden method (dotnet#56337)
  Prevent Segfault in EventPipe on disable (dotnet#56104)
  Update runtimeconfig.json and deps.json paths when these break past the MAX_PATH threshold  (dotnet#56224)
  Use native allocator instead of pinning when decompressing embedded PDB (dotnet#56336)
  Specify win-x64 as a valid platform in the microsoft-net-runtime-* workloads for iOS/tvOS/MacCatalyst (dotnet#56311)
  Fix FailFast message formatting race (dotnet#56388)
  Try to fix finalizer-based async tests (dotnet#56384)
  Fix MetricsEventSource tests (dotnet#56382)
  Remove invalid Castle.DynamicProxy.Internal.AbstractInvocation from ILLink descriptor files (dotnet#56392)
  ...
@ghost ghost locked as resolved and limited conversation to collaborators Aug 27, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

We should be able serialize and deserialize from DOM
3 participants