C#中Newtonsoft.Json 到 System.Text.Json 遷移避坑指南
1. 核心設(shè)計(jì)哲學(xué)差異
在進(jìn)行代碼遷移前,必須牢記這兩個(gè)庫(kù)在底層設(shè)計(jì)哲學(xué)上的根本分歧,這是幾乎所有反序列化報(bào)錯(cuò)的根源:
- Newtonsoft.Json (主打兼容與靈活) :它非常寬容,會(huì)盡最大努力去猜測(cè)你的意圖,在底層默默幫你做各種隱式的類型轉(zhuǎn)換和容錯(cuò)處理。
- System.Text.Json (主打性能與安全) :微軟為了追求極致的執(zhí)行效率而原生打造。它極其嚴(yán)格,要求 JSON 數(shù)據(jù)結(jié)構(gòu)和 C# 模型“嚴(yán)絲合縫”,絕不會(huì)越界替你做任何類型轉(zhuǎn)換。
2. 基礎(chǔ)特性與配置替換對(duì)照表
| 場(chǎng)景 | Newtonsoft.Json (舊) | System.Text.Json (新) | 遷移備注 |
|---|---|---|---|
| 指定 JSON 鍵名 | [JsonProperty("name")] | [JsonPropertyName("name")] | 必須逐個(gè)替換。 |
| 忽略某字段 | [JsonIgnore] | [JsonIgnore] | 基本一致。 |
| 忽略空值 (Null) | NullValueHandling.Ignore | 全局 Options: DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull | 推薦在全局 JsonSerializerOptions 中統(tǒng)一配置,減少序列化體積。 |
| 忽略默認(rèn)值 | DefaultValueHandling.Ignore | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | 新版將其合并到了 JsonIgnore 特性中。 |
3. 四大高頻“踩坑”重災(zāi)區(qū)及標(biāo)準(zhǔn)解決方案
?? 坑一:大小寫嚴(yán)格敏感 (Case Sensitivity)
問(wèn)題描述:很多第三方 API 返回的小駝峰命名(如 userProfile),而 C# 模型是大駝峰命名(如 UserProfile)。老版能完美自動(dòng)映射,新版只要大小寫不一致,直接反序列化為 null。
解決方案:在反序列化時(shí),務(wù)必全局傳入配置允許忽略大小寫:
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var result = JsonSerializer.Deserialize<MyModel>(jsonString, options);
?? 坑二:基礎(chǔ)類型嚴(yán)格匹配(數(shù)字與字符串的鴻溝)
問(wèn)題描述:對(duì)接外部不可控 API 時(shí)經(jīng)常遇到格式不規(guī)范的數(shù)據(jù)。比如 ID 字段有時(shí)是數(shù)字 ("id": 123),有時(shí)是字符串 ("id": "A-123");金額字段有時(shí)返回字符串 ("price": "19.99"),甚至用空字符串表示無(wú)數(shù)據(jù) ("discount": "")。
新版只要遇到 JSON 節(jié)點(diǎn)類型與 C# 聲明類型(如 string 對(duì) int)不匹配,會(huì)直接拋出 JsonException 崩潰。
解決方案:不要指望內(nèi)置配置項(xiàng)能完美兜底(尤其是處理空字符串),建議直接封裝自定義 JsonConverter。
??? 通用工具 1:數(shù)字安全轉(zhuǎn)字符串轉(zhuǎn)換器 (NumberToStringConverter)
用途:當(dāng) C# 模型定義為 string Id,但外部 JSON 傳入的是數(shù)字 123 時(shí),自動(dòng)將其轉(zhuǎn)換為 "123" 且不報(bào)錯(cuò)。
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace YourNamespace.Helpers
{
public class NumberToStringConverter : JsonConverter<string?>
{
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number) return reader.GetInt64().ToString();
if (reader.TokenType == JsonTokenType.String) return reader.GetString();
return null;
}
public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options)
{
if (value == null) writer.WriteNullValue();
else writer.WriteStringValue(value);
}
}
}
// 實(shí)體類使用方式:[JsonConverter(typeof(NumberToStringConverter))]
??? 通用工具 2:字符串安全轉(zhuǎn)可空金額轉(zhuǎn)換器 (StringToDecimalConverter)
用途:當(dāng) C# 模型定義為 decimal? Price,但 JSON 傳入的是 "19.99" 或者是代表無(wú)值的空字符串 "" 時(shí),安全地將其轉(zhuǎn)換為 decimal 或 null。
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace YourNamespace.Helpers
{
// 注意:泛型必須與屬性類型完全一致(此處為可空類型 decimal?)
public class StringToDecimalConverter : JsonConverter<decimal?>
{
public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number) return reader.GetDecimal();
if (reader.TokenType == JsonTokenType.String)
{
string? strValue = reader.GetString();
// 很多老舊 API 喜歡用空字符串代表沒(méi)有值,安全處理為 null
if (string.IsNullOrWhiteSpace(strValue)) return null;
if (decimal.TryParse(strValue, out decimal result)) return result;
}
return null;
}
public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options)
{
// 序列化時(shí),可根據(jù)對(duì)接方 API 的偏好決定是否轉(zhuǎn)回字符串
if (value.HasValue) writer.WriteStringValue(value.Value.ToString("0.00"));
else writer.WriteNullValue();
}
}
}
// 實(shí)體類使用方式:[JsonConverter(typeof(StringToDecimalConverter))]
// 警告:此轉(zhuǎn)換器必須配合 public decimal? Price { get; set; } 使用!不可用于非空 decimal。
?? 坑三:動(dòng)態(tài)類型object的解析陷阱
問(wèn)題描述:當(dāng)模型中存在 public object Value { get; set; }(例如用于接收不確定結(jié)構(gòu)的數(shù)據(jù)、擴(kuò)展字段 metadata 等),老版會(huì)猜測(cè)并轉(zhuǎn)化為 string, int 等具體 C# 基礎(chǔ)類型。而新版會(huì)統(tǒng)一將其解析為 JsonElement 結(jié)構(gòu)體。
危險(xiǎn)操作:任何試圖將反序列化后的 object 強(qiáng)轉(zhuǎn)回基礎(chǔ)類型的操作都會(huì)導(dǎo)致運(yùn)行時(shí)崩潰!
- ?
string val = (string)model.Value;-> 拋出 InvalidCastException - ?
if (model.Value is string s)-> 永遠(yuǎn)為 false - ?
string val = model.Value as string;-> 永遠(yuǎn)返回 null
安全的操作規(guī)范(提取真實(shí)數(shù)據(jù)) :
純中轉(zhuǎn)/序列化/拼接場(chǎng)景(最穩(wěn)妥) :利用
Convert.ToString()提取字面量。// 完美應(yīng)對(duì) JsonElement。 // 配合 InvariantCulture 防止部署在不同國(guó)家服務(wù)器時(shí),小數(shù)點(diǎn)被轉(zhuǎn)換成逗號(hào)的問(wèn)題。 string safeStringValue = Convert.ToString(model.Value, CultureInfo.InvariantCulture)!;
業(yè)務(wù)邏輯需嚴(yán)格執(zhí)行類型判斷:通過(guò)檢查
JsonElement.ValueKind。if (model.Value is JsonElement element) { if (element.ValueKind == JsonValueKind.String) string s = element.GetString(); else if (element.ValueKind == JsonValueKind.Number) decimal d = element.GetDecimal(); }
?? 坑四:字段 (Fields) 被靜默忽略
問(wèn)題描述:老版會(huì)自動(dòng)序列化和反序列化 public string name; 這種公開的字段 (Fields)。新版默認(rèn)只處理屬性 (Properties) ,即帶有 { get; set; } 的成員,對(duì)字段直接靜默忽略,不報(bào)錯(cuò)但數(shù)據(jù)會(huì)全部丟失。
解決方案:
最佳實(shí)踐:將實(shí)體類的成員強(qiáng)制重構(gòu)為標(biāo)準(zhǔn)屬性
{ get; set; }。兼容方案:若存在大量歷史代碼難以修改,需在全局 Options 中顯式開啟:
var options = new JsonSerializerOptions { IncludeFields = true };
4. 遷移與調(diào)試的黃金法則
在未來(lái)遇到 System.Text.Json 拋出異?;蚍葱蛄谢?null 時(shí),請(qǐng)嚴(yán)格遵循以下排查步驟:
- 絕不盲猜數(shù)據(jù)結(jié)構(gòu):不要依賴 API 文檔。務(wù)必將 response.Content.ReadAsStreamAsync() 臨時(shí)替換為 ReadAsStringAsync(),把原始 JSON 字符串完整打印到日志中,肉眼確認(rèn)真實(shí)的層級(jí)和數(shù)據(jù)格式。
- 檢查大小寫配置:確認(rèn)代碼中是否遺漏了 PropertyNameCaseInsensitive = true 的配置。
- 檢查類型嚴(yán)格性:排查 JSON 里的 "123" 和 C# 里的 int 是否發(fā)生了直接碰撞,若有,必須引入 Converter。
- 檢查轉(zhuǎn)換器泛型匹配:貼在可空類型屬性上的轉(zhuǎn)換器,其繼承的基類絕對(duì)不能是非空類型(如 JsonConverter<decimal?> 絕不能用于 decimal 屬性),必須分毫不差。
到此這篇關(guān)于C#中Newtonsoft.Json 到 System.Text.Json 遷移避坑指南的文章就介紹到這了,更多相關(guān)C#中Newtonsoft.Json到System.Text.Json 遷移內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C#下Newtonsoft.Json的具體使用
- C# Newtonsoft.Json庫(kù)的常用屬性和方法詳解
- C#使用Newtonsoft.Json庫(kù)實(shí)現(xiàn)JSON數(shù)據(jù)中某個(gè)字段值的提取功能
- C# Newtonsoft.Json用法詳解
- C#使用Newtonsoft.Json中的JObject對(duì)象
- C# newtonsoft.json中文亂碼問(wèn)號(hào)的解決方案
- c# Newtonsoft.Json 常用方法總結(jié)
- C# Newtonsoft.Json 解析多嵌套json 進(jìn)行反序列化的實(shí)例
- c#添加Newtonsoft.Json包的操作
- C# Newtonsoft.Json 的使用說(shuō)明
相關(guān)文章
使用C#代碼實(shí)現(xiàn)將圖片插入到Excel中
圖片是一種直觀、高效的信息表達(dá)方式,在實(shí)際工作中,常常需要在 Excel 報(bào)告中插入圖片,本文將以 Spire.XLS for .NET 為例,介紹如何使用 C# 和 VB.NET 在 Excel 文檔中插入圖片,有需要的小伙伴可以了解下2026-01-01
C#.NET?ConcurrentBag<T>?設(shè)計(jì)原理與使用場(chǎng)景
ConcurrentBag<T>?是System.Collections.Concurrent?命名空間下的線程安全的無(wú)序集合,本文就來(lái)詳細(xì)的介紹一下C#.NET?ConcurrentBag<T>?原理與使用,感興趣的可以了解一下2026-01-01
C#使用Free?Spire.PDF進(jìn)行PDF打印的實(shí)現(xiàn)方案
在現(xiàn)代應(yīng)用開發(fā)中,打印?PDF?文件是一個(gè)常見(jiàn)需求,C#?提供了多種庫(kù)來(lái)支持這一功能,其中?Free?Spire.PDF?for?.NET?是一個(gè)不錯(cuò)的選擇,本文將深入解析如何使用?Free?Spire.PDF?進(jìn)行?PDF?打印,需要的朋友可以參考下2025-08-08
automation服務(wù)器不能創(chuàng)建對(duì)象 解決方法
本文主要介紹如何解決“automation服務(wù)器不能創(chuàng)建對(duì)象”錯(cuò)誤,從而解決Visual Studio.Net不能正常使用的問(wèn)題,需要的朋友可以參考下。2016-06-06
C#借助Spire.XLS?for?.NET實(shí)現(xiàn)一鍵移除Excel條件格式
在日常開發(fā)中,我們經(jīng)常會(huì)遇到需要處理?Excel?文件的場(chǎng)景,本文將介紹如何借助?Spire.XLS?for?.NET?在?C#?中高效地移除這些條件格式,有需要的可以了解下2026-03-03
C#編程實(shí)現(xiàn)向并口設(shè)備發(fā)送指令、獲取并口設(shè)備的狀態(tài)
這篇文章主要介紹了C#編程實(shí)現(xiàn)向并口設(shè)備發(fā)送指令、獲取并口設(shè)備的狀態(tài),本文直接給出實(shí)例代碼,需要的朋友可以參考下2015-06-06

