From a3da178fb6a3c837dd3030f19ff302738d58dcb0 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 13 Jun 2025 10:11:02 -0500 Subject: [PATCH 1/3] Remove Dependency on Newtonsoft.Json From Protocol objects When trimming or native AOT'ing an app, we want to remove Newtonsoft.Json dependencies when possible because it brings in a lot of AOT warnings and adds a lot of size to the final app. Begin by removing these simple cases in the ToStrings of JsonRpcError, Request, and Result. ToString methods of instantiated types can never be trimmed, so the code in them is always preserved. --- src/StreamJsonRpc/Protocol/JsonRpcError.cs | 16 +++---- src/StreamJsonRpc/Protocol/JsonRpcRequest.cs | 10 ++--- src/StreamJsonRpc/Protocol/JsonRpcResult.cs | 8 ++-- src/StreamJsonRpc/RequestId.cs | 5 +++ .../Protocol/JsonRpcRequestTests.cs | 44 +++++++++++++++++-- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/StreamJsonRpc/Protocol/JsonRpcError.cs b/src/StreamJsonRpc/Protocol/JsonRpcError.cs index 7905eb811..d1007f547 100644 --- a/src/StreamJsonRpc/Protocol/JsonRpcError.cs +++ b/src/StreamJsonRpc/Protocol/JsonRpcError.cs @@ -3,8 +3,8 @@ using System.Diagnostics; using System.Runtime.Serialization; +using System.Text.Json.Nodes; using StreamJsonRpc.Reflection; -using JsonNET = Newtonsoft.Json.Linq; using STJ = System.Text.Json.Serialization; namespace StreamJsonRpc.Protocol; @@ -51,15 +51,15 @@ public object? Id /// public override string ToString() { - return new JsonNET.JObject + return new JsonObject { - new JsonNET.JProperty("id", this.RequestId.ObjectValue), - new JsonNET.JProperty("error", new JsonNET.JObject + ["id"] = this.RequestId.AsJsonValue(), + ["error"] = new JsonObject { - new JsonNET.JProperty("code", this.Error?.Code), - new JsonNET.JProperty("message", this.Error?.Message), - }), - }.ToString(Newtonsoft.Json.Formatting.None); + ["code"] = this.Error?.Code is not null ? JsonValue.Create(this.Error?.Code) : null, + ["message"] = this.Error?.Message, + }, + }.ToJsonString(); } /// diff --git a/src/StreamJsonRpc/Protocol/JsonRpcRequest.cs b/src/StreamJsonRpc/Protocol/JsonRpcRequest.cs index c41239ac6..904b21d2e 100644 --- a/src/StreamJsonRpc/Protocol/JsonRpcRequest.cs +++ b/src/StreamJsonRpc/Protocol/JsonRpcRequest.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.Serialization; -using JsonNET = Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; using STJ = System.Text.Json.Serialization; namespace StreamJsonRpc.Protocol; @@ -285,10 +285,10 @@ public virtual bool TryGetArgumentByNameOrIndex(string? name, int position, Type /// public override string ToString() { - return new JsonNET.JObject + return new JsonObject { - new JsonNET.JProperty("id", this.RequestId.ObjectValue), - new JsonNET.JProperty("method", this.Method), - }.ToString(Newtonsoft.Json.Formatting.None); + ["id"] = this.RequestId.AsJsonValue(), + ["method"] = this.Method, + }.ToJsonString(); } } diff --git a/src/StreamJsonRpc/Protocol/JsonRpcResult.cs b/src/StreamJsonRpc/Protocol/JsonRpcResult.cs index 6bd3157e6..83ac903c6 100644 --- a/src/StreamJsonRpc/Protocol/JsonRpcResult.cs +++ b/src/StreamJsonRpc/Protocol/JsonRpcResult.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.Runtime.Serialization; -using JsonNET = Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; using STJ = System.Text.Json.Serialization; namespace StreamJsonRpc.Protocol; @@ -71,10 +71,10 @@ public object? Id /// public override string ToString() { - return new JsonNET.JObject + return new JsonObject { - new JsonNET.JProperty("id", this.RequestId.ObjectValue), - }.ToString(Newtonsoft.Json.Formatting.None); + ["id"] = this.RequestId.AsJsonValue(), + }.ToJsonString(); } /// diff --git a/src/StreamJsonRpc/RequestId.cs b/src/StreamJsonRpc/RequestId.cs index 5185a2503..eda0a62b1 100644 --- a/src/StreamJsonRpc/RequestId.cs +++ b/src/StreamJsonRpc/RequestId.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Globalization; +using System.Text.Json.Nodes; using Newtonsoft.Json; namespace StreamJsonRpc; @@ -122,4 +123,8 @@ internal static RequestId Parse(object? value) value is int i ? new RequestId(i) : throw new JsonSerializationException("Unexpected type for id property: " + value.GetType().Name); } + + internal JsonValue? AsJsonValue() => + this.Number is not null ? JsonValue.Create(this.Number.Value) : + JsonValue.Create(this.String); } diff --git a/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs b/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs index b7062130b..4928a34af 100644 --- a/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs +++ b/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using StreamJsonRpc.Protocol; -using Xunit; - public class JsonRpcRequestTests { private static readonly IReadOnlyList ArgumentsAsList = new List { 4, 6, 8 }; @@ -81,4 +78,45 @@ public void DefaultRequestDoesNotSupportTopLevelProperties() Assert.False(request.TryGetTopLevelProperty("test", out string? value)); } #pragma warning restore CS0618 // Type or member is obsolete + + [Fact] + public void ToString_Works() + { + var data = new JsonRpcRequest + { + RequestId = new RequestId(10), + Method = "t", + }; + + Assert.Equal( + """{"id":10,"method":"t"}""", + data.ToString()); + } + + [Fact] + public void JsonRpcError_ToString_Works() + { + var data = new JsonRpcError + { + RequestId = new RequestId(1), + Error = new JsonRpcError.ErrorDetail { Code = JsonRpcErrorCode.InternalError, Message = "some error" }, + }; + + Assert.Equal( + """{"id":1,"error":{"code":-32603,"message":"some error"}}""", + data.ToString()); + } + + [Fact] + public void JsonRpcResult_ToString_Works() + { + var data = new JsonRpcResult + { + RequestId = new RequestId("id"), + }; + + Assert.Equal( + """{"id":"id"}""", + data.ToString()); + } } From 500fce083b224fc9acf46e20e6f5fa8386f42767 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 13 Jun 2025 11:56:35 -0500 Subject: [PATCH 2/3] Cast JsonRpcErrorCode to int to avoid trim/aot warnings on using an enum. --- src/StreamJsonRpc/Protocol/JsonRpcError.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StreamJsonRpc/Protocol/JsonRpcError.cs b/src/StreamJsonRpc/Protocol/JsonRpcError.cs index d1007f547..b673edcba 100644 --- a/src/StreamJsonRpc/Protocol/JsonRpcError.cs +++ b/src/StreamJsonRpc/Protocol/JsonRpcError.cs @@ -56,7 +56,7 @@ public override string ToString() ["id"] = this.RequestId.AsJsonValue(), ["error"] = new JsonObject { - ["code"] = this.Error?.Code is not null ? JsonValue.Create(this.Error?.Code) : null, + ["code"] = this.Error?.Code is not null ? JsonValue.Create((int)this.Error.Code) : null, ["message"] = this.Error?.Message, }, }.ToJsonString(); From a9bfef44c0e14fab1ab1e271d284f3525242de93 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 16 Jun 2025 10:20:28 -0500 Subject: [PATCH 3/3] PR feedback --- test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs b/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs index 4928a34af..bb1d3877b 100644 --- a/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs +++ b/test/StreamJsonRpc.Tests/Protocol/JsonRpcRequestTests.cs @@ -80,7 +80,7 @@ public void DefaultRequestDoesNotSupportTopLevelProperties() #pragma warning restore CS0618 // Type or member is obsolete [Fact] - public void ToString_Works() + public void JsonRpcRequest_ToString_Works() { var data = new JsonRpcRequest {