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

Initial serialized member name mapping #13527

Merged
merged 14 commits into from
Aug 6, 2020
Merged

Conversation

heaths
Copy link
Member

@heaths heaths commented Jul 16, 2020

This is the prototype implementation for serialized member name mappings we discussed. The other commits will be squashed out if/when merged into master.

@ghost ghost added the Azure.Core label Jul 16, 2020
@heaths heaths added the Client This issue points to a problem in the data-plane of the library. label Jul 16, 2020
Copy link
Member

@tg-msft tg-msft left a comment

Choose a reason for hiding this comment

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

Looks like a good start. We should definitely get the names/locations of projects right before merging to avoid any unnecessary confusion for customers who pay attention to what's happening in the repo.

@@ -0,0 +1,49 @@
# Newtonsoft.Json implementation for Azure Core Experimental shared client library for .NET
Copy link
Member

Choose a reason for hiding this comment

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

Let's start a thread, but my understanding is the name for this package should be Microsoft.[Feature].[Dependency] and would expect it to be more like Microsoft.ObjectSerializer.NewtonsoftJson. We might also include the version if we want to avoid breaks. +@KrzysztofCwalina

Copy link
Member

Choose a reason for hiding this comment

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

I also think this should probably go in the /sdk/extensions/ folder instead of /sdk/core/

Copy link
Contributor

@pakrym pakrym Jul 17, 2020

Choose a reason for hiding this comment

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

I disagree, we would need a place to put GeoJSON converter implementation for newtonsoft too. Not sure if it's wise to ship a package for every tiny feature. Or we can say the "feature" here is Azure.Core integration.

And I would keep it next to core for the same reason.

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 started a thread, but unless we reach agreement soon will keep this as-is for now.

Copy link
Member

Choose a reason for hiding this comment

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

I think the GeoJSON converter belongs somewhere else. We don't want everyone using Newtonsoft to have to pull in a reference to Microsoft.Spatial. I'd prefer Microsoft.ObjectSerializer.NewtonsoftJson and Microsoft.ObjectSerializer.MicrosoftSpatial as separate packages.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, sorry, I thought you were talking about support for #13165 as well. We'll need System.Text.Json and Newtonsoft.Json converters for those spatial types.

I think Microsoft.ObjectSerializer.NewtonsoftJson could hold both an implementation of ObjectSerializer and Newtonsoft converters for any Azure.Core types. Maybe ObjectSerializer isn't a broad enough feature name and we should use something more like Microsoft.Serialization.NewtonsoftJson?

Copy link
Contributor

Choose a reason for hiding this comment

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

What if we do some JsonPatch support later that would require Newtonsoft adapters.

I would like the understand benefits of having feature-scoped packages instead of something more generic and keeping a leeway for the future.

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 agree with @pakrym. I don't understand what benefit not having a 1:1 relationship with Azure.Core with an assembly for Newtonsoft.Json. Think of it this way: that 1 assembly is the Newtonsoft.Json implementations for Azure.Core. If it weren't already taboo, I'd actually think something like Azure.Core.Newtonsoft(.)Json would be appropriate, just for comparison.

Copy link
Member

Choose a reason for hiding this comment

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

I would consider ObjectSerializer a feature and Azure.Core a library. We have approval for Microsoft.[Feature].[Dependency]. Microsoft.Azure.Core.[Dependency] feels confusing given all the Microsoft.Azure.* Track 1 libraries.
We should discuss it with the Krzysztof and the Arch Board.

Copy link
Member Author

Choose a reason for hiding this comment

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

@AlexGhiondea also agrees with the current plane. @KrzysztofCwalina, what do you think?

@tg-msft
Copy link
Member

tg-msft commented Jul 17, 2020

(+@ahsonkhan if he has time for a sanity check)

Copy link
Contributor

@pakrym pakrym left a comment

Choose a reason for hiding this comment

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

Pending comments.

@ahsonkhan
Copy link
Member

ahsonkhan commented Jul 21, 2020

Follow up to the above comment #13527 (comment)

The following code snippet won't work when the stream starts with a UTF-8 BOM.

using var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return JsonSerializer.Deserialize(memoryStream.ToArray(), returnType, _options);

The synchronous, JsonSerializer.Deserialize method that accepts a ReadOnlySpan<byte> expects only valid JSON data (without any trivia or artifacts). The BOM is considered invalid JSON and will fail.

The async, stream accepting API explicitly skips the BOM for you (to support things like file stream). For this workaround, you would have to explicitly skip the BOM yourself.

https://github.com/dotnet/runtime/blob/e87fbd1030b472b4bc44c375434da65611aa4df0/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs#L159-L164

var utf8 = new byte[5] {0xEF, 0xBB, 0xBF, (byte)'{', (byte)'}'};
object obj = JsonSerializer.Deserialize<object>(utf8);

/*
Unhandled exception. System.Text.Json.JsonException: '0xEF' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
 ---> System.Text.Json.JsonReaderException: '0xEF' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
   at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
*/

This is why I asked if accepting input streams that could contain a BOM is important to support here.

There is an existing request to add the sync stream-accepting APIs to S.T.J, but until then, you'd need to address it yourself as a workaround:
dotnet/runtime#1574

As an aside, we can avoid the extra allocation and .ToArray() call in that method, if performance is important, if the stream is seek-able and we have access to ArrayBufferWriter<T>, i.e. running on core (which should be true in most cases):
https://docs.microsoft.com/en-us/dotnet/api/system.buffers.arraybufferwriter-1?view=netcore-3.1

public static object Deserialize(Stream stream, Type returnType)
{
  if (stream.CanSeek)
  {
    int dataLength = (int)stream.Length;
    var abw = new ArrayBufferWriter<byte>(dataLength);
    Span<byte> span = abw.GetSpan(dataLength);
    
    while(true)
    {
      int bytesWritten = stream.Read(span);
      if (bytesWritten == 0) break;
      abw.Advance(bytesWritten);
      span = span.Slice(bytesWritten);
    }

    ReadOnlySpan<byte> written = abw.WrittenSpan;
    int start = 0;
    // TODO: if first byte is start of utf-8 bom, check the next 3 bytes, and then skip them.
    written = written.Slice(start);

    return JsonSerializer.Deserialize(written, returnType);
  }

  using var memoryStream = new MemoryStream();
  stream.CopyTo(memoryStream);
   // TODO: if first byte is start of utf-8 bom, check the next 3 bytes, and then skip them.
  return JsonSerializer.Deserialize(memoryStream.ToArray(), returnType);
}

@heaths
Copy link
Member Author

heaths commented Jul 23, 2020

@ahsonkhan I opened #13698 to track since it's out of scope for this PR.

@heaths
Copy link
Member Author

heaths commented Jul 23, 2020

ObjectSerializer and friends are being moved to Azure.Core in PR #13619 and this interface isn't yet board-approved. Will need to retarget/rebase once approved.

@pakrym
Copy link
Contributor

pakrym commented Aug 6, 2020

This might need to be updated to include the new package: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/ci.yml#L38

.devcontainer/Dockerfile Outdated Show resolved Hide resolved
@heaths
Copy link
Member Author

heaths commented Aug 6, 2020

This might need to be updated to include the new package: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/ci.yml#L38

Are we planning on shipping it just yet? I didn't expect so. If not, I'll open an issue to track releasing it.

@heaths heaths requested a review from mitchdenny as a code owner August 6, 2020 18:24
@heaths heaths merged commit c3e2a57 into Azure:master Aug 6, 2020
@heaths heaths deleted the proto-name-mapping branch August 20, 2020 03:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Azure.Core Client This issue points to a problem in the data-plane of the library. Do Not Merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants