In .Net 6, when trying to use SignalR with JsonProtocol (System.Text.Json), my client program throws the following exception
System.IO.InvalidDataException: Error reading JSON.
---> System.Text.Json.JsonException: '{' is invalid after a value. Expected either ',', '}', or ']'. Path: $ | LineNumber: 0 | BytePositionInLine: 4090
Code on server side:
services.AddSignalR()
.AddJsonProtocol();
Code on client side:
conn = new HubConnectionBuilder()
.WithUrl($"{_serverBaseAddress}mainHub", o =>
{
o.AccessTokenProvider = InitializeOrRefreshAccessTokenIfNeeded;
o.Transports = HttpTransportType.WebSockets;
}
)
.ConfigureLogging(logging =>
{
logging.AddConsole();
})
.AddJsonProtocol()
.Build()
Note: everything works fine when using NewtonsoftJsonProtocol:
Code on server:
services.AddSignalR()
.AddNewtonsoftJsonProtocol(opts =>
{
opts.PayloadSerializerSettings.TypeNameHandling = TypeNameHandling.All;
});
Code on client:
conn = new HubConnectionBuilder()
.WithUrl($"{_serverBaseAddress}mainHub", o =>
{
o.AccessTokenProvider = InitializeOrRefreshAccessTokenIfNeeded;
o.Transports = HttpTransportType.WebSockets;
}
)
.ConfigureLogging(logging =>
{
logging.AddConsole();
})
.AddNewtonsoftJsonProtocol(
opts =>
opts.PayloadSerializerSettings.TypeNameHandling = TypeNameHandling.All
)
.Build();
There is a difference, with NewtonsoftJson I specify TypeNameHandling.All but I do not know any equivalent for System.Text.Json.
Why not just keep Newtonsoft.Json? I would like to use source generators and specify a SerializerContext like below:
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.AddContext<MyJsonSerializerContext>();
});
which I believe I cannot do with NewtonsoftJson
As it turned out, switching from Newtonsoft.Json to System.Text.Json is not as simple as just switching protocols in the configuration.
There is this page from Microsoft that describes the key differences between the 2 and helps with the migration: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to?pivots=dotnet-6-0
As they explain, in some scenarios it will not be possible to move to System.Text.Json.
So in my case, I was looking at errors on the client but actually, the server was throwing a cycling error like this one: How to JSON serialize without cyclic error Replacing [IgnoreDataMember] by [JsonIgnore] fixes the issue.
Also TypeNameHandling.All which I was using in Newtonsoft.Json is not supported in System.Text.Json. I was using this setting because of polymorphic deserialization, but in this case, they propose to write custom converters as a workaround: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-6-0#support-polymorphic-deserialization