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

Use System.Text.Json for serialization #10551

Merged
merged 26 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5da723f
Progress commit
davidwengier May 30, 2024
e70d1b9
All tests passing
davidwengier May 31, 2024
a1656ba
Remove some random Newtonsoft.Json references, and get tests passing …
davidwengier May 31, 2024
c4244d0
Remove some old serialization attributes
davidwengier May 31, 2024
191adb6
Found a sneaky params object
davidwengier May 31, 2024
069d5a7
Fix generated document communications
davidwengier Jun 3, 2024
20da0bd
Allow code actions to talk Newtonsoft as well, since thats what Rosly…
davidwengier Jun 4, 2024
b1f3906
Fix inlay hints
davidwengier Jun 5, 2024
7fbee1e
Fixes
davidwengier Jun 17, 2024
39a19a7
Fix build
davidwengier Jun 17, 2024
0f9263f
Add dual attributes so we can freely communicate with WebTools
davidwengier Jun 27, 2024
ea2cda8
Missed a TODO
davidwengier Jun 27, 2024
d17b4ff
Keep RazorTextChange shape in line with TextChange, and ServerTextCha…
davidwengier Jun 28, 2024
9d0f82a
PR feedback (and a little logging tweak)
davidwengier Jun 28, 2024
ec197b7
Merge remote-tracking branch 'upstream/main' into dev/dawengie/STJ
davidwengier Jun 30, 2024
36f28be
Cleanup
davidwengier Jul 2, 2024
efa67a4
Cleanup
davidwengier Jul 2, 2024
44688f6
Merge remote-tracking branch 'upstream/main' into dev/dawengie/STJ
davidwengier Jul 2, 2024
5981abd
Update all the packages!!
davidwengier Jul 2, 2024
ca2bba2
Nullability
davidwengier Jul 2, 2024
fb2ab65
Explicit null check because the VSSDK analyzer is fussy
davidwengier Jul 3, 2024
d70c96c
Merge remote-tracking branch 'upstream/main' into dev/dawengie/STJ
davidwengier Jul 3, 2024
fd6d6cc
Try bump some more packages
davidwengier Jul 3, 2024
db9efe4
Try publish binlogs from mac and linux
davidwengier Jul 3, 2024
c61b0d5
More slightly educated guesses at package versioning
davidwengier Jul 3, 2024
66a18d7
Add package source mapping
davidwengier Jul 3, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.IO;
using System.Text;
using System.Text.Json;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer;
Expand All @@ -11,15 +11,13 @@
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json;

namespace Microsoft.AspNetCore.Razor.Microbenchmarks.Serialization;

public class CompletionListSerializationBenchmark
{
private readonly byte[] _completionListBuffer;

private readonly JsonSerializer _serializer;
private readonly CompletionList _completionList;

public CompletionListSerializationBenchmark()
Expand All @@ -29,8 +27,6 @@ public CompletionListSerializationBenchmark()
var optionsMonitor = new RazorLSPOptionsMonitor(configurationService, RazorLSPOptions.Default);
var tagHelperCompletionProvider = new TagHelperCompletionProvider(completionService, optionsMonitor);

_serializer = JsonSerializer.Create();

var documentContent = "<";
var queryIndex = 1;
_completionList = GenerateCompletionList(documentContent, queryIndex, tagHelperCompletionProvider);
Expand All @@ -43,36 +39,32 @@ public void ComponentElement_CompletionList_Serialization_RoundTrip()
// Serialize back to json.
MemoryStream originalStream;
using (originalStream = new MemoryStream())
using (var writer = new StreamWriter(originalStream, Encoding.UTF8, bufferSize: 4096))
{
_serializer.Serialize(writer, _completionList);
JsonSerializer.Serialize(originalStream, _completionList);
}

CompletionList deserializedCompletions;
var stream = new MemoryStream(originalStream.GetBuffer());
using (stream)
using (var reader = new JsonTextReader(new StreamReader(stream)))
{
deserializedCompletions = _serializer.Deserialize<CompletionList>(reader).AssumeNotNull();
deserializedCompletions = JsonSerializer.Deserialize<CompletionList>(stream).AssumeNotNull();
}
}

[Benchmark(Description = "Component Completion List Serialization")]
public void ComponentElement_CompletionList_Serialization()
{
using var stream = new MemoryStream();
using var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 4096);
_serializer.Serialize(writer, _completionList);
JsonSerializer.Serialize(stream, _completionList);
}

[Benchmark(Description = "Component Completion List Deserialization")]
public void ComponentElement_CompletionList_Deserialization()
{
// Deserialize from json file.
using var stream = new MemoryStream(_completionListBuffer);
using var reader = new JsonTextReader(new StreamReader(stream));
CompletionList deserializedCompletions;
deserializedCompletions = _serializer.Deserialize<CompletionList>(reader).AssumeNotNull();
deserializedCompletions = JsonSerializer.Deserialize<CompletionList>(stream).AssumeNotNull();
}

private CompletionList GenerateCompletionList(string documentContent, int queryIndex, TagHelperCompletionProvider componentCompletionProvider)
Expand Down Expand Up @@ -111,8 +103,7 @@ private CompletionList GenerateCompletionList(string documentContent, int queryI
private byte[] GenerateBuffer(CompletionList completionList)
{
using var stream = new MemoryStream();
using var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 4096);
_serializer.Serialize(writer, completionList);
JsonSerializer.Serialize(stream, completionList);
var buffer = stream.GetBuffer();

return buffer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;
davidwengier marked this conversation as resolved.
Show resolved Hide resolved

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
Expand All @@ -12,7 +13,6 @@
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Newtonsoft.Json.Linq;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;

Expand Down Expand Up @@ -101,7 +101,7 @@ public DefaultCSharpCodeActionProvider(LanguageServerFeatureOptions languageServ
// flag is on, any perf hit here isn't going to affect real users.
try
{
if (((JToken)codeAction.Data).ToObject<RazorCodeActionResolutionParams>() is not null)
if (((JsonElement)codeAction.Data).Deserialize<RazorCodeActionResolutionParams>() is not null)
Copy link
Member

Choose a reason for hiding this comment

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

This construct is a little strange. I did a bit of a double take when I saw it. 😄

Consider adding a helper to clarify this. Could that be used elsewhere? Maybe something like this?

public static bool CanDeserializeTo<T>(this JsonElement jsonElement)
    => jsonElement.Deserialize<T> is not null;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm happy to do that in a local function, but I don't really want to put it on a help class anywhere, because I would hate for anyone to think that its a useful function to call. This line is only hit when a feature flag is on, that is only for our information, and only possible to turn on for MS internal users, so the badness of the code doesn't worry me here.

{
// This code action has already been wrapped by something else, so skip it here, or it could
// be marked as experimental when its not, and more importantly would be duplicated in the list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
Expand All @@ -22,7 +23,6 @@
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;
using StreamJsonRpc;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
Expand Down Expand Up @@ -215,9 +215,9 @@ private RazorVSInternalCodeAction[] ExtractCSharpCodeActionNamesFromData(RazorVS

foreach (var codeAction in codeActions)
{
// Note: we may see a perf benefit from using a JsonConverter
var tags = ((JToken?)codeAction.Data)?["CustomTags"]?.ToObject<string[]>();
if (tags is null || tags.Length == 0)
if (codeAction.Data is not JsonElement jsonData ||
!jsonData.TryGetProperty("CustomTags", out var value) ||
value.Deserialize<string[]>() is not [..] tags)
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
{
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;

Expand Down Expand Up @@ -66,13 +66,13 @@ public async Task<CodeAction> HandleRequestAsync(CodeAction request, RazorReques
throw new ArgumentNullException(nameof(request));
}

if (request.Data is not JObject paramsObj)
if (request.Data is not JsonElement paramsObj)
{
_logger.LogError($"Invalid CodeAction Received '{request.Title}'.");
return request;
}

var resolutionParams = paramsObj.ToObject<RazorCodeActionResolutionParams>();
var resolutionParams = paramsObj.Deserialize<RazorCodeActionResolutionParams>();

if (resolutionParams is null)
{
Expand All @@ -87,7 +87,7 @@ public async Task<CodeAction> HandleRequestAsync(CodeAction request, RazorReques
// as it does not support Command.Edit based code actions anymore.
if (resolutionParams.Action == LanguageServerConstants.CodeActions.EditBasedCodeActionCommand)
{
request.Edit = (resolutionParams.Data as JObject)?.ToObject<WorkspaceEdit>();
request.Edit = (resolutionParams.Data as JsonElement?)?.Deserialize<WorkspaceEdit>();
return request;
}

Expand Down Expand Up @@ -128,7 +128,7 @@ internal async Task<CodeAction> ResolveRazorCodeActionAsync(
return codeAction;
}

if (resolutionParams.Data is not JObject data)
if (resolutionParams.Data is not JsonElement data)
{
return codeAction;
}
Expand All @@ -148,20 +148,20 @@ internal Task<CodeAction> ResolveHtmlCodeActionAsync(CodeAction codeAction, Razo

private async Task<CodeAction> ResolveDelegatedCodeActionAsync(ImmutableDictionary<string, BaseDelegatedCodeActionResolver> resolvers, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken)
{
if (resolutionParams.Data is not JObject csharpParamsObj)
if (resolutionParams.Data is not JsonElement csharpParamsObj)
{
_logger.LogError($"Invalid CodeAction Received.");
Debug.Fail($"Invalid CSharp CodeAction Received.");
return codeAction;
}

var csharpParams = csharpParamsObj.ToObject<CodeActionResolveParams>();
var csharpParams = csharpParamsObj.Deserialize<CodeActionResolveParams>();
if (csharpParams is null)
{
throw new ArgumentOutOfRangeException($"Data was not convertible to {nameof(CodeActionResolveParams)}");
}

codeAction.Data = csharpParams.Data as JToken;
codeAction.Data = csharpParams.Data;

if (!resolvers.TryGetValue(resolutionParams.Action, out var resolver))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Text.Json.Serialization;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;

internal sealed class AddUsingsCodeActionParams
{
[JsonPropertyName("uri")]
public required Uri Uri { get; set; }
[JsonPropertyName("namespace")]
public required string Namespace { get; set; }
[JsonPropertyName("additionalEdit")]
public TextDocumentEdit? AdditionalEdit { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;

Expand All @@ -26,19 +27,19 @@ public static SumType<Command, CodeAction> AsVSCodeCommandOrCodeAction(this VSIn
razorCodeAction = new VSInternalCodeAction()
{
Title = razorCodeAction.Title,
Data = JToken.FromObject(resolutionParams),
Data = JsonSerializer.SerializeToElement(resolutionParams),
TelemetryId = razorCodeAction.TelemetryId,
};
}

var serializedParams = JToken.FromObject(razorCodeAction.Data);
var arguments = new JArray(serializedParams);
var serializedParams = JsonSerializer.SerializeToNode(razorCodeAction.Data).AssumeNotNull();
var arguments = new JsonArray(serializedParams);

return new Command
{
Title = razorCodeAction.Title ?? string.Empty,
CommandIdentifier = LanguageServerConstants.RazorCodeActionRunnerCommand,
Arguments = arguments.ToArray(),
Arguments = arguments.ToArray()!
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand Down Expand Up @@ -71,7 +72,7 @@ public static RazorVSInternalCodeAction WrapResolvableCodeAction(
Language = language,
Data = resolveParams
};
razorCodeAction.Data = JToken.FromObject(resolutionParams);
razorCodeAction.Data = JsonSerializer.SerializeToElement(resolutionParams);

if (!isOnAllowList)
{
Expand Down Expand Up @@ -108,7 +109,7 @@ private static VSInternalCodeAction WrapResolvableCodeAction(
Language = language,
Data = resolveParams
};
razorCodeAction.Data = JToken.FromObject(resolutionParams);
razorCodeAction.Data = JsonSerializer.SerializeToElement(resolutionParams);

if (!isOnAllowList)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.Text.Json.Serialization;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;

internal sealed class CodeActionResolveParams
{
[JsonPropertyName("data")]
public object? Data { get; set; }

// Need to use the VS type so that project context info, if present, is maintained
[JsonPropertyName("razorFileIdentifier")]
public required VSTextDocumentIdentifier RazorFileIdentifier { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Text.Json.Serialization;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;

internal sealed class CreateComponentCodeActionParams
{
[JsonPropertyName("uri")]
public required Uri Uri { get; set; }
[JsonPropertyName("path")]
public required string Path { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Text.Json.Serialization;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;

internal sealed class ExtractToCodeBehindCodeActionParams
{
[JsonPropertyName("uri")]
public required Uri Uri { get; set; }

[JsonPropertyName("extractStart")]
public int ExtractStart { get; set; }

[JsonPropertyName("extractEnd")]
public int ExtractEnd { get; set; }

[JsonPropertyName("removeStart")]
public int RemoveStart { get; set; }

[JsonPropertyName("removeEnd")]
public int RemoveEnd { get; set; }

[JsonPropertyName("namespace")]
public required string Namespace { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Text.Json.Serialization;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;

internal class GenerateMethodCodeActionParams
{
[JsonPropertyName("uri")]
public required Uri Uri { get; set; }

[JsonPropertyName("methodName")]
public required string MethodName { get; set; }

[JsonPropertyName("eventName")]
public required string EventName { get; set; }

[JsonPropertyName("isAsync")]
public required bool IsAsync { get; set; }
}
Loading