.NET 4.8下ASP.NET Web API替换Newtonsoft.Json为System.Text.Json的兼容改造问题
看起来你遇到的核心问题是:.NET 4.8的ASP.NET Web API默认依赖Newtonsoft.Json做模型绑定,哪怕你自己的Data类已经切换到System.Text.Json,Web API在反序列化请求体为Data.Request对象时,还是会用Newtonsoft,导致内部动态属性被反序列化成JObject而非你期望的JsonElement。
不用纠结于“为什么Newtonsoft还在参与”——这是Web API框架的默认行为,我们需要手动替换掉它的JSON格式化器,同时保持向后兼容性,下面是具体的落地步骤:
1. 准备依赖
首先在你的Web API项目中安装System.Text.Json NuGet包(选择兼容.NET Framework 4.8的版本,比如v6.0.x及以上,这些版本对4.8支持良好)。
2. 自定义System.Text.Json媒体格式化器
ASP.NET Web API通过MediaTypeFormatter处理请求/响应的序列化/反序列化,我们需要实现一个基于System.Text.Json的格式化器,替换掉默认的Newtonsoft实现:
using System.IO; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Text; using System.Text.Json; public class SystemTextJsonMediaTypeFormatter : MediaTypeFormatter { private readonly JsonSerializerOptions _serializerOptions; public SystemTextJsonMediaTypeFormatter(JsonSerializerOptions options = null) { // 配置序列化选项,尽量和原Newtonsoft行为对齐,保证向后兼容 _serializerOptions = options ?? new JsonSerializerOptions { PropertyNameCaseInsensitive = true, // 和Newtonsoft默认一致,忽略大小写匹配 PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 按需调整,和原项目命名策略对齐 WriteIndented = false, // 生产环境建议关闭缩进,和Newtonsoft默认一致 Converters = { new JsonStringEnumConverter() } // 枚举序列化为字符串,对齐Newtonsoft默认行为 }; // 声明支持application/json媒体类型 SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); // 支持常用编码 SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); SupportedEncodings.Add(UnicodeEncoding.Unicode); } public override bool CanReadType(Type type) => true; // 支持所有类型的反序列化 public override bool CanWriteType(Type type) => true; // 支持所有类型的序列化 public override async Task<object> ReadFromStreamAsync( Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { using var reader = new StreamReader(readStream, SupportedEncodings.First()); var jsonContent = await reader.ReadToEndAsync(); return JsonSerializer.Deserialize(jsonContent, type, _serializerOptions); } public override async Task WriteToStreamAsync( Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext) { var jsonContent = JsonSerializer.Serialize(value, type, _serializerOptions); using var writer = new StreamWriter(writeStream, SupportedEncodings.First()); await writer.WriteAsync(jsonContent); } }
3. 替换Web API的默认格式化器
在Web API项目的Global.asax.cs的Application_Start方法中,替换默认的Newtonsoft格式化器为我们自定义的:
protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); // 移除默认的Newtonsoft JSON格式化器 var defaultJsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter; if (defaultJsonFormatter != null) { GlobalConfiguration.Configuration.Formatters.Remove(defaultJsonFormatter); } // 添加自定义的System.Text.Json格式化器 GlobalConfiguration.Configuration.Formatters.Add(new SystemTextJsonMediaTypeFormatter()); }
4. 关键细节对齐(保证向后兼容)
为了让新旧客户端都能正常工作,需要确保System.Text.Json的序列化行为和原Newtonsoft完全对齐:
- 命名策略:如果原项目用PascalCase(类属性默认的大写开头),就把
PropertyNamingPolicy设为null;如果用驼峰,保持JsonNamingPolicy.CamelCase。 - 空值处理:如果原Newtonsoft忽略空值属性,就在
JsonSerializerOptions中设置DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull。 - 特殊类型转换:如果有自定义类型的序列化逻辑,需要对应实现System.Text.Json的
JsonConverter来替代原Newtonsoft的JsonConverter。
为什么这比“先反序列再重新序列化”更好?
你提到的临时方案(用Newtonsoft反序列后再转System.Text.Json)确实能凑活,但会带来双重序列化/反序列化的性能损耗,而且会让代码变得冗余难维护。替换格式化器是从框架层面解决问题,一次性搞定所有请求/响应的序列化逻辑,更干净也更高效。
兼容性验证
- 新客户端(用System.Text.Json序列化请求):Web API用System.Text.Json反序列化,内部属性会是
JsonElement,完全符合你的预期。 - 旧客户端(用Newtonsoft序列化请求):只要JSON是标准格式(没有Newtonsoft特有的扩展比如
$type),System.Text.Json就能正常反序列化,不会影响旧功能。 - 响应兼容性:Web API用System.Text.Json序列化响应返回
HttpResponseMessage,不管是新客户端用System.Text.Json还是旧客户端用Newtonsoft,都能正常解析标准JSON。
内容来源于stack exchange

