Fix C# 14 breaking change - add explicit string casts for JsonSerializer.Deserialize calls
Overview
This PR addresses a breaking change in C# 14 (.NET 10) related to JsonSerializer.Deserialize method overload resolution. Starting with .NET 10, new overloads accepting ReadOnlySpan<byte> are available, which can cause ambiguity errors when the compiler cannot determine which overload to use.
Problem
As documented in the C# 14 compiler breaking changes, code that passes parameters of ambiguous types (like object, var, or nullable types) to JsonSerializer.Deserialize will fail to compile with errors such as:
The call is ambiguous between the following methods or properties:
'JsonSerializer.Deserialize<TValue>(ReadOnlySpan<byte>, JsonSerializerOptions?)' and
'JsonSerializer.Deserialize<TValue>(string, JsonSerializerOptions?)'
Solution
Added explicit (string) casts to all JsonSerializer.Deserialize calls where parameters could potentially be ambiguous:
Modified Files
-
JsonBodyModelBinder.cs
- Cast
jsonPayloadparameter to ensure string overload is selected
- Cast
-
SettingService.cs
- Cast
setting.Metadataproperty in both generic (Deserialize<T>) and non-generic calls - Metadata is stored as JSON strings in the database
- Cast
-
PowerExcelMapper.cs
- Cast
cell.StringCellValuewhen deserializing JSON data from Excel cells
- Cast
-
SessionExtensions.cs
- Cast
valuefromGetString()which returnsstring?(nullable string)
- Cast
Testing
- ✅ Full solution builds successfully with no warnings or errors
- ✅ Infrastructure tests pass (69/69)
- ✅ Web.Common tests pass (11/11)
- ✅ All other
JsonSerializer.Deserializecalls verified as safe
Compatibility
These changes are purely defensive and maintain full backward compatibility with .NET 8 and .NET 9. The explicit casts clarify intent and prepare the codebase for .NET 10 without affecting current functionality.
Related
Fixes #[issue_number] (if applicable)
Thanks to @CancanTang for the early heads-up on this breaking change during .NET 10 smoke testing!
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
ccsca2021.crl.certum.pl
- Triggering command:
dotnet build GrandNode.sln --no-incremental(dns block)ccsca2021.ocsp-certum.com
- Triggering command:
dotnet build GrandNode.sln --no-incremental(dns block)crl.certum.pl
- Triggering command:
dotnet build GrandNode.sln --no-incremental(dns block)subca.ocsp-certum.com
- Triggering command:
dotnet build GrandNode.sln --no-incremental(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
Original prompt
This section details on the original issue you should resolve
<issue_title>C# 14 breaking change heads up</issue_title> <issue_description>Hey folks, as part of our smoke testing for .NET 10, we noticed that grandnode2 is impacted by a breaking change in .NET 10 related to https://github.com/dotnet/roslyn/blob/main/docs/compilers/CSharp/Compiler%20Breaking%20Changes%20-%20DotNet%2010.md#spant-and-readonlyspant-overloads-are-applicable-in-more-scenarios-in-c-14-and-newer. When building on dotnet-sdk-10.0.100-rc.2.25474.116, you may encounter errors like:
The call is ambiguous between the following methods or properties: 'JsonSerializer.Deserialize<TValue>(ReadOnlySpan<byte>, JsonSerializerOptions?)' and 'JsonSerializer.Deserialize<TValue>(string, JsonSerializerOptions?)'This occurs because, starting with .NET 10, the overloads for JsonSerializer.Deserialize were updated, and code that passes a parameter of type 'object' or 'var' (where the type could be either string or byte span) can no longer be resolved automatically by the compiler. This leads to build failures.
To address this, you’ll need to update your code to explicitly cast the parameter to either string or ReadOnlySpan
when calling JsonSerializer.Deserialize, as appropriate. For example: C# // For string input
JsonSerializer.Deserialize<TValue>((string)input, options);// For byte input
JsonSerializer.Deserialize<TValue>((ReadOnlySpan<byte>)input, options);Alternatively, ensure that your code always passes the correct type matching the method signature, or upgrade any dependencies that may be affected by this change.Please get details from https://github.com/dotnet/sdk/issues/51022 if you need them.
Just wanted to give a heads-up so you can plan for a smooth transition to .NET 10!</issue_description>
Comments on the Issue (you are @copilot in this section)
@KrzysztofPajak @CancanTang Thanks for catching this and for the detailed breakdown! We’ll keep an eye on this and start making the necessary adjustments once .NET 10 is officially out. Really appreciate the early notice — that’ll definitely save us some headaches later 🙂
Fixes grandnode/grandnode2#626
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.
Quality Gate failed
Failed conditions
C Maintainability Rating on New Code (required ≥ A)
See analysis details on SonarQube Cloud
Catch issues before they fail your Quality Gate with our IDE extension
SonarQube for IDE