Skip to content

Commit ac3431e

Browse files
authored
Remove dependency on Newtonsoft.Json from Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost (#81562)
1 parent 8916598 commit ac3431e

File tree

7 files changed

+28
-28
lines changed

7 files changed

+28
-28
lines changed

src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@
4444
<ItemGroup>
4545
<PackageReference Include="Microsoft.Build.Locator" PrivateAssets="All" />
4646
<PackageReference Include="System.Collections.Immutable" />
47-
<PackageReference Include="Newtonsoft.Json" />
4847
<PackageReference Include="Microsoft.IO.Redist" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
4948
<PackageReference Include="System.Threading.Tasks.Extensions" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
49+
<PackageReference Include="System.Text.Json" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
5050
</ItemGroup>
5151
<ItemGroup>
5252
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\*.cs" Link="InternalUtilities\%(FileName).cs" />

src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,30 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Text;
6-
using Newtonsoft.Json;
7-
using Newtonsoft.Json.Serialization;
6+
using System.Text.Encodings.Web;
7+
using System.Text.Json;
8+
using System.Text.Json.Serialization;
9+
using System.Text.Unicode;
810

911
namespace Microsoft.CodeAnalysis.MSBuild;
1012

1113
internal static class JsonSettings
1214
{
1315
public static readonly Encoding StreamEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
1416

15-
public static readonly JsonSerializerSettings SingleLineSerializerSettings = new()
17+
public static readonly JsonSerializerOptions SingleLineSerializerOptions = new()
1618
{
17-
// Setting Formatting.None ensures each is serialized to it's own line, which we implicitly rely on
18-
Formatting = Newtonsoft.Json.Formatting.None,
19-
2019
// We use nulls for optional things, so doesn't matter
21-
NullValueHandling = NullValueHandling.Ignore,
20+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
2221

23-
ContractResolver = new CamelCasePropertyNamesContractResolver(),
22+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
2423

2524
// We escape all non-ASCII characters. Because we're writing to stdin/stdout, on Windows codepages could be set that might interfere with Unicode, especially on build machines.
26-
// By escaping all non-ASCII it means the JOSN stream itself is ASCII and thus can't be impacted by codepage issues.
27-
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
25+
// By escaping all non-ASCII it means the JSON stream itself is ASCII and thus can't be impacted by codepage issues.
26+
// JavaScriptEncoder.Create(UnicodeRanges.BasicLatin) only allows BasicLatin (ASCII 0x00-0x7F) to be unescaped.
27+
Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin),
28+
29+
// Ensure WriteIndented is false (which is the default) so each message is serialized to its own line
30+
WriteIndented = false
2831
};
2932
}

src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/Request.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Immutable;
6-
using Newtonsoft.Json.Linq;
6+
using System.Text.Json;
77

88
namespace Microsoft.CodeAnalysis.MSBuild;
99

@@ -24,5 +24,5 @@ internal sealed class Request
2424
/// </summary>
2525
public required string Method { get; init; }
2626

27-
public required ImmutableArray<JToken> Parameters { get; init; }
27+
public required ImmutableArray<JsonElement> Parameters { get; init; }
2828
}

src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/Response.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using Newtonsoft.Json.Linq;
5+
using System.Text.Json;
66

77
namespace Microsoft.CodeAnalysis.MSBuild;
88

99
internal sealed class Response
1010
{
1111
public int Id { get; init; }
12-
public JToken? Value { get; init; }
12+
public JsonElement? Value { get; init; }
1313
public string? Exception { get; init; }
1414
}
1515

src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
using System.IO;
99
using System.IO.Pipes;
1010
using System.Reflection;
11+
using System.Text.Json;
1112
using System.Threading;
1213
using System.Threading.Tasks;
13-
using Newtonsoft.Json;
14-
using Newtonsoft.Json.Linq;
1514
using Roslyn.Utilities;
1615

1716
namespace Microsoft.CodeAnalysis.MSBuild;
@@ -68,7 +67,7 @@ public async Task RunAsync()
6867

6968
try
7069
{
71-
request = JsonConvert.DeserializeObject<Request>(line);
70+
request = JsonSerializer.Deserialize<Request>(line, JsonSettings.SingleLineSerializerOptions);
7271
Contract.ThrowIfNull(request);
7372
}
7473
catch (Exception e)
@@ -133,7 +132,7 @@ private async Task ProcessRequestAsync(Request request)
133132
if (i == methodParameters.Length - 1 && lastParameterIsCancellationToken)
134133
arguments[i] = CancellationToken.None;
135134
else
136-
arguments[i] = request.Parameters[i].ToObject(methodParameters[i].ParameterType);
135+
arguments[i] = request.Parameters[i].Deserialize(methodParameters[i].ParameterType, JsonSettings.SingleLineSerializerOptions);
137136
}
138137

139138
var result = method.Invoke(rpcTarget, arguments);
@@ -156,7 +155,7 @@ private async Task ProcessRequestAsync(Request request)
156155
}
157156
}
158157

159-
response = new Response { Id = request.Id, Value = result is not null ? JToken.FromObject(result) : null };
158+
response = new Response { Id = request.Id, Value = result is not null ? JsonSerializer.SerializeToElement(result, JsonSettings.SingleLineSerializerOptions) : null };
160159
}
161160
catch (Exception e)
162161
{
@@ -166,7 +165,7 @@ private async Task ProcessRequestAsync(Request request)
166165
response = new Response { Id = request.Id, Exception = $"An exception of type {e.GetType()} was thrown: {e.Message}" };
167166
}
168167

169-
var responseJson = JsonConvert.SerializeObject(response, JsonSettings.SingleLineSerializerSettings);
168+
var responseJson = JsonSerializer.Serialize(response, JsonSettings.SingleLineSerializerOptions);
170169

171170
#if DEBUG
172171
// Assert we didn't put a newline in this, since if we did the receiving side won't know how to parse it

src/Workspaces/MSBuild/Core/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
<PackageReference Include="Microsoft.Build.Framework" />
2626
<PackageReference Include="Microsoft.Extensions.Logging" />
2727
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" />
28-
<PackageReference Include="Newtonsoft.Json" />
2928
<!--
3029
Since System.Text.Json is part of the .NET BCL we do not want to add
3130
it as a package reference. Doing so will pin the version to what we use

src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
using System.Collections.Generic;
88
using System.IO;
99
using System.IO.Pipes;
10+
using System.Text.Json;
1011
using System.Threading;
1112
using System.Threading.Tasks;
12-
using Newtonsoft.Json;
13-
using Newtonsoft.Json.Linq;
1413
using Roslyn.Utilities;
1514

1615
namespace Microsoft.CodeAnalysis.MSBuild;
@@ -60,7 +59,7 @@ public void Start()
6059
Response? response;
6160
try
6261
{
63-
response = JsonConvert.DeserializeObject<Response>(line);
62+
response = JsonSerializer.Deserialize<Response>(line, JsonSettings.SingleLineSerializerOptions);
6463
}
6564
catch (JsonException ex)
6665
{
@@ -89,7 +88,7 @@ public void Start()
8988
try
9089
{
9190
// response.Value might be null if the response was in fact null.
92-
var result = response.Value?.ToObject(expectedType);
91+
var result = response.Value?.Deserialize(expectedType, JsonSettings.SingleLineSerializerOptions);
9392
completionSource.SetResult(result);
9493
}
9594
catch (Exception ex)
@@ -155,10 +154,10 @@ public async Task<T> InvokeAsync<T>(int targetObject, string methodName, List<ob
155154
Id = requestId,
156155
TargetObject = targetObject,
157156
Method = methodName,
158-
Parameters = parameters.SelectAsArray(static p => p is not null ? JToken.FromObject(p) : JValue.CreateNull())
157+
Parameters = parameters.SelectAsArray(static p => JsonSerializer.SerializeToElement(p, JsonSettings.SingleLineSerializerOptions))
159158
};
160159

161-
var requestJson = JsonConvert.SerializeObject(request, JsonSettings.SingleLineSerializerSettings) + Environment.NewLine;
160+
var requestJson = JsonSerializer.Serialize(request, JsonSettings.SingleLineSerializerOptions) + Environment.NewLine;
162161
var requestJsonBytes = JsonSettings.StreamEncoding.GetBytes(requestJson);
163162

164163
try

0 commit comments

Comments
 (0)