從 Newtonsoft.Json 移轉至 System.Text.Json
本文顯示如何從 Newtonsoft.Json 移轉至 System.Text.Json。
System.Text.Json
命名空間提供序列化為 JavaScript 物件標記法 (JSON) 以及從 JSON 還原序列化的功能。 此 System.Text.Json
程式庫包含在 .NET Core 3.1 及更新版本的執行階段中。 對於其他目標 Framework,請安裝 System.Text.Json NuGet 套件。 該套件支援下列版本:
- .NET Standard 2.0 及更新版本
- .NET framework 4.6.2 及更新版本
- .NET Core 2.0、2.1 與 2.2
提示
System.Text.Json
主要著重於效能、安全性與標準合規性。 其預設行為有一些關鍵差異,且具有 Newtonsoft.Json
的同位功能。 在某些案例中,System.Text.Json
目前沒有內建功能,但具有建議的因應措施。 除此以外的其他案例,建議的因應措施並不適用。
System.Text.Json
團隊正在投入資源,以新增最常收到要求的功能。 若應用程式相依於某個遺漏的功能,請考慮在 dotnet/runtime GitHub 存放庫中提出問題,以了解是否可以新增此案例的支援。
本文大部分是關於如何使用 JsonSerializer API 的內容,但也包含如何使用 JsonDocument (表示文件物件模型,或稱 DOM)、Utf8JsonReader 與 Utf8JsonWriter 類型的指導。
在 Visual Basic 中,您無法使用 Utf8JsonReader,這也表示您無法撰寫自訂轉換器。 此處顯示的大部分因應措施都需要您撰寫自訂轉換器。 您可以使用 C# 撰寫自訂轉換器,並在 Visual Basic 專案中加以註冊。 如需詳細資訊,請參閱 Visual Basic 支援。
差異資料表
下列資料表列出 Newtonsoft.Json
功能與 System.Text.Json
對應項。 對應項的分類如下:
- ✔️ 內建功能即支援。 從
System.Text.Json
取得類似的行為,可能需要使用屬性或全域選項。 -
⚠ 不支援,但有因應措施。 因應措施為自訂轉換器,這可能無法提供具有
Newtonsoft.Json
功能的完整同位項目。 針對其中某些部分,範例程式碼會以範例的形式提供。 若您依賴這些Newtonsoft.Json
功能,則移轉會需要修改 .NET 物件模型或其他程式碼變更。 -
❌ 不支援,且因應措施不適用或沒有因應措施。 若您依賴這些
Newtonsoft.Json
功能,則無法在沒有重大變更的情況下進行移轉。
Newtonsoft.Json 功能 | System.Text.Json equivalent |
---|---|
根據預設,不區分大小寫的還原序列化 | ✔️ PropertyNameCaseInsensitive 全域設定 |
駝峰式大小寫屬性名稱 | ✔️ PropertyNamingPolicy 全域設定 |
蛇形命名法屬性名稱 | ✔️ 蛇形命名原則 |
最小字元逸出 | ✔️ 嚴格的字元逸出規定,可設定 |
NullValueHandling.Ignore 全域設定 |
✔️ DefaultIgnoreCondition 全域選項 |
允許註解 | ✔️ ReadCommentHandling 全域設定 |
允許尾端逗號 | ✔️ AllowTrailingCommas 全域設定 |
自訂轉換器註冊 | ✔️ 優先順序不同 |
預設的深度上限為 64,可設定 | ✔️ 預設的深度上限為 64,可設定 |
PreserveReferencesHandling 全域設定 |
✔️ ReferenceHandling 全域設定 |
將引號中的數字序列化或還原序列化 | ✔️ NumberHandling 全域設定、[JsonNumberHandling] 屬性 |
還原序列化為不可變類別和結構 | ✔️ JsonConstructor、C# 9 記錄 |
支援欄位 | ✔️ IncludeFields 全域設定、[JsonInclude] 屬性 |
DefaultValueHandling 全域設定 |
✔️ DefaultIgnoreCondition 全域設定 |
NullValueHandling 上的 [JsonProperty] 設定 |
✔️ JsonIgnore 屬性 |
DefaultValueHandling 上的 [JsonProperty] 設定 |
✔️ JsonIgnore 屬性 |
使用非字串索引鍵將 Dictionary 還原序列化 |
✔️ 支援 |
支援非公用屬性 setter 與 getter | ✔️ JsonInclude 屬性 |
[JsonConstructor] 屬性 |
✔️ [JsonConstructor] 屬性 |
ReferenceLoopHandling 全域設定 |
✔️ ReferenceHandling 全域設定 |
回撥 | ✔️ 回呼 |
NAN、Infinity、-Infinity | ✔️ 支援 |
Required 屬性上的 [JsonProperty] 設定 |
✔️ [JsonRequired] 屬性與 C# 必要修飾詞 |
DefaultContractResolver 可忽略屬性 |
✔️ DefaultJsonTypeInfoResolver 類別 |
多型序列化 | ✔️ [JsonDerivedType] 屬性 |
多型還原序列化 | ✔️ [JsonDerivedType] 屬性上的類型鑑別子 |
還原序列化字串列舉值 | ✔️ 還原序列化字串列舉值 |
MissingMemberHandling 全域設定 |
✔️ 處理遺漏的成員 |
在沒有 setter 的情況下填入屬性 | ✔️ 在沒有 setter 的情況下填入屬性 |
ObjectCreationHandling 全域設定 |
✔️ 重複使用而非取代屬性 |
支援廣泛的類型 | ⚠️ 部分類型需要自訂轉換器 |
將推斷的類型還原序列化為 object 屬性 |
️⚠不支援、因應措施、範例 |
將 JSON null 常值還原序列化為不可為 Null 的實值型別 |
️⚠不支援、因應措施、範例 |
DateTimeZoneHandling 、DateFormatString 設定 |
️⚠不支援、因應措施、範例 |
JsonConvert.PopulateObject 方法 |
⚠️ 不支援、因應措施 |
支援 System.Runtime.Serialization 屬性 |
️⚠不支援、因應措施、範例 |
JsonObjectAttribute |
⚠️ 不支援、因應措施 |
允許沒有引號的屬性名稱 | ❌ 設計不支援 |
允許字串值周圍的單引號 | ❌ 設計不支援 |
允許字串屬性的非字串 JSON 值 | ❌ 設計不支援 |
TypeNameHandling.All 全域設定 |
❌ 設計不支援 |
支援 JsonPath 查詢 |
❌ 不支援 |
可設定的限制 | ❌ 不支援 |
這不是 Newtonsoft.Json
功能的完整清單。 此清單包含 GitHub 問題或 StackOverflow 文章中要求的許多案例。 若您針對此處列出的其中一個案例實作因應措施,但目前沒有範例程式碼,且您想要共用解決方案,請在此頁面底部的 [意見反應] 區段中選取 [此頁面]。 這會在此文件的 GitHub 存放庫中建立問題,並列在此頁面的 [意見反應] 區段中。
預設行為的差異
根據預設,System.Text.Json 為嚴格,並會避免代表呼叫者進行任何猜測或解譯,以強調具決定性行為。 程式庫刻意以此種方式設計,以獲得效能與安全性。 根據預設,Newtonsoft.Json
很彈性。 這般設計的基本差異存在於下列預設行為的許多特定差異中。
不區分大小寫的還原序列化
根據預設,在還原序列化期間,Newtonsoft.Json
會執行不區分大小寫的屬性名稱比對。
System.Text.Json 預設會區分大小寫,因為其執行完全相符的比對,所以可提供更佳的效能。 如需如何執行不區分大小寫比對的資訊,請參閱不區分大小寫的屬性比對。
若您正在藉由使用 ASP.NET Core 來間接使用 System.Text.Json
,則不需要執行任何動作,即可取得類似 Newtonsoft.Json
的行為。 當 ASP.NET Core 使用 System.Text.Json
時,其會指定使用駝峰式大小寫屬性名稱與不區分大小寫比對的設定。
根據預設,ASP.NET Core 也會啟用還原序列化的引號數字。
最小字元逸出
在序列化期間,Newtonsoft.Json
對於讓字元在不逸出的情況下通過,標準相對寬鬆。 也就是說,其不會以 \uxxxx
取代字元,其中 xxxx
是字元的字碼指標。 其會在字元之前發出 \
來進行逸出 (例如,"
變成 \"
)。 根據預設,System.Text.Json 會逸出更多字元,以便針對跨網站指令碼 (XSS) 或資訊洩漏攻擊提供深層防禦保護,並使用六個字元順序來執行此動作。 根據預設,System.Text.Json
會逸出所有非 ASCII 字元;因此,若您在 StringEscapeHandling.EscapeNonAscii
中使用 Newtonsoft.Json
,則不需要執行任何動作。 根據預設,System.Text.Json
也會逸出 HTML 敏感性字元。 如需如何覆寫預設 System.Text.Json
行為的資訊,請參閱自訂字元編碼。
註解
根據預設,在還原序列化期間,Newtonsoft.Json
會忽略 JSON 中的註解。 因為 System.Text.Json 規格不包含註解的例外狀況,所以 預設會擲回這些例外狀況。 如需如何允許註解的資訊,請參閱允許註解與尾端逗號。
尾端逗號
根據預設,在還原序列化期間,Newtonsoft.Json
會忽略尾端逗號。 其也會忽略多個尾端逗號 (例如 [{"Color":"Red"},{"Color":"Green"},,]
)。 因為 System.Text.Json 規格不允許尾端逗號的例外狀況,所以 預設會擲回這些例外狀況。 如需如何使 System.Text.Json
接受尾端逗號的資訊,請參閱允許註解與尾端逗號。 不允許多個尾端逗號。
轉換器註冊優先順序
自訂轉換器的 Newtonsoft.Json
註冊優先順序如下:
- 屬性 (property) 上的屬性 (attribute)
- 類型上的屬性
- 轉換器 (英文) 集合
此順序表示 Converters
集合中的自訂轉換器,是由在類型層級中套用屬性所註冊的轉換器所覆寫。 這兩個註冊都會由屬性 (property) 層級的屬性 (attribute) 所覆寫。
自訂轉換器的 System.Text.Json 註冊優先順序有所不同:
- 屬性 (property) 上的屬性 (attribute)
- Converters 集合
- 類型上的屬性
此處差異在於 Converters
集合中的自訂轉換器會覆寫類型層級的屬性。 此優先順序背後的意圖是讓執行階段變更覆寫設計階段選擇。 無法變更優先順序。
如需自訂轉換器註冊的詳細資訊,請參閱註冊自訂轉換器。
深度上限
根據預設,Newtonsoft.Json
的最新版本深度上限為 64。
System.Text.Json 也有 64 的預設限制,且可藉由設定 JsonSerializerOptions.MaxDepth 來設定。
若您藉由使用 ASP.NET Core 來間接使用 System.Text.Json
,則預設的深度上限為 32。 預設值與模型繫結相同,且是在 JsonOptions 類別 (英文) 中設定。
JSON 字串 (屬性名稱與字串值)
在還原序列化期間,Newtonsoft.Json
接受以雙引號、單引號或沒有引號括住的屬性名稱。 其會接受以雙引號或單引號括住的字串值。 例如,Newtonsoft.Json
會接受下列 JSON:
{
"name1": "value",
'name2': "value",
name3: 'value'
}
因為 System.Text.Json
規格需要置於雙引號內的屬性名稱與字串值,且是唯一視為有效 JSON 的格式,所以 只接受該格式。
以單引號括住的值會產生具有下列訊息的 JsonException:
''' is an invalid start of a value.
字串屬性的非字串值
Newtonsoft.Json
會接受非字串值 (例如數字或常值 true
與 false
),以便還原序列化為類型字串的屬性。 下列是成功將 Newtonsoft.Json
還原序列化為下列類別的 JSON 範例:
{
"String1": 1,
"String2": true,
"String3": false
}
public class ExampleClass
{
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
}
System.Text.Json
不會將非字串值還原序列化為字串屬性。 針對字串欄位收到的非字串值,會產生 JsonException 並顯示下列訊息:
The JSON value could not be converted to System.String.
使用 JsonSerializer 的案例
內建功能不支援下列部分案例,但可能會有因應措施。 因應措施為自訂轉換器,這可能無法提供具有 Newtonsoft.Json
功能的完整同位項目。 針對其中某些部分,範例程式碼會以範例的形式提供。 若您依賴這些 Newtonsoft.Json
功能,則移轉會需要修改 .NET 物件模型或其他程式碼變更。
在下列部分案例中,因應措施不適用或沒有因應措施。 若您依賴這些 Newtonsoft.Json
功能,則無法在沒有重大變更的情況下進行移轉。
允許或撰寫引號中的數字
Newtonsoft.Json
可將 JSON 字串所代表的數字 (以引號括住) 序列化或還原序列化。 例如,其可以接受:{"DegreesCelsius":"23"}
,而不是 {"DegreesCelsius":23}
。 若要在 System.Text.Json 中啟用該行為,請將 JsonSerializerOptions.NumberHandling 設定為 WriteAsString 或 AllowReadingFromString,或使用 [JsonNumberHandling] 屬性。
若您正在藉由使用 ASP.NET Core 來間接使用 System.Text.Json
,則不需要執行任何動作,即可取得類似 Newtonsoft.Json
的行為。 ASP.NET Core 使用 時,會指定 System.Text.Json
,且 Web 預設值允許引號數字。
如需詳細資訊,請參閱允許或撰寫引號中的數字。
指定還原序列化時要使用的建構函式
Newtonsoft.Json
[JsonConstructor]
屬性可讓您指定要在還原序列化為 POCO 時呼叫的建構函式。
System.Text.Json
也有 [JsonConstructor] 屬性。 如需詳細資訊,請參閱不可變的類型與記錄。
有條件地忽略屬性
Newtonsoft.Json
有數種方式可以有條件地忽略序列化或還原序列化的屬性:
-
DefaultContractResolver
可讓您根據任意準則來選取要包含或忽略的屬性。 -
NullValueHandling
上的DefaultValueHandling
與JsonSerializerSettings
設定,可讓您指定應忽略的所有 null 值或預設值屬性。 -
NullValueHandling
屬性 (attribute) 上的DefaultValueHandling
與[JsonProperty]
設定,可讓您指定當設定為 Null 或預設值時應該忽略的個別屬性 (property)。
System.Text.Json 會提供下列在序列化時忽略屬性或欄位的方式:
- 屬性 (property) 上的 [JsonIgnore] 屬性 (attribute) 會導致在序列化期間從 JSON 省略屬性 (property)。
- IgnoreReadOnlyProperties 全域選項可讓您忽略所有唯讀屬性。
- 若您會包含欄位,則 JsonSerializerOptions.IgnoreReadOnlyFields 全域選項可讓您忽略所有唯讀欄位。
-
DefaultIgnoreCondition
全域選項可讓您忽略具有預設值的所有實值型別屬性,或忽略所有具有 null 值的參考型別屬性。
此外,在 .NET 7 及更新版本中,您可以自訂 JSON 合約,以根據任意準則忽略屬性。 如需詳細資訊,請參閱自訂合約。
公用與非公用欄位
Newtonsoft.Json
可將欄位與屬性序列化及還原序列化。
在 System.Text.Json 中,使用 JsonSerializerOptions.IncludeFields 全域設定或 [JsonInclude] 屬性,在序列化或還原序列化時包含公用欄位。 如需範例,請參閱包含欄位。
保留物件參考與處理迴圈
根據預設,Newtonsoft.Json
會依據值進行序列化。 例如,若物件包含兩個屬性,且屬性中包含相同 Person
物件的參考,則該 Person
物件的屬性值就會在 JSON 中重複。
Newtonsoft.Json
在 PreserveReferencesHandling
上具有 JsonSerializerSettings
設定,可讓您以傳址方式序列化:
- 識別碼中繼資料會新增至為第一個
Person
物件所建立的 JSON。 - 為第二個
Person
物件建立的 JSON 包含該識別碼的參考,而不是屬性值。
Newtonsoft.Json
也有一個 ReferenceLoopHandling
設定,可讓您忽略循環參考,而不是擲回例外狀況。
若要在 System.Text.Json 中保留參考及處理循環參考,請將 JsonSerializerOptions.ReferenceHandler 設定為 Preserve。
ReferenceHandler.Preserve
設定相當於 PreserveReferencesHandling
中的 = PreserveReferencesHandling.All
Newtonsoft.Json
。
ReferenceHandler.IgnoreCycles
選項具有類似於 Newtonsoft.JsonReferenceLoopHandling.Ignore
的行為。 其中一個差異在於,System.Text.Json 實作會以 null
JSON 權杖取代參考迴圈,而不是忽略物件參考。 如需詳細資訊,請參閱忽略循環參考。
如同 Newtonsoft.JsonReferenceResolver,System.Text.Json.Serialization.ReferenceResolver 類別會定義在序列化與還原序列化上保留參考的行為。 建立衍生類別以指定自訂行為。 如需範例,請參閱 GuidReferenceResolver (英文)。
不支援某些相關的 Newtonsoft.Json
功能:
如需詳細資訊,請參閱保留參考及處理循環參考。
具有非字串索引鍵的字典
Newtonsoft.Json
與 System.Text.Json
都支援 Dictionary<TKey, TValue>
類型的集合。 如需支援金鑰類型的詳細資訊,請參閱 支援的金鑰類型。
警告
還原序列化為 Dictionary<TKey, TValue>
,其中 TKey
類型為 string
以外的任何項目可能會造成取用應用程式中的安全性弱點。 如需詳細資訊,請參閱 dotnet/runtime#4761 (英文)。
沒有內建支援的類型
System.Text.Json 不提供下列類型的內建支援:
- DataTable 和相關類型(如需詳細資訊,請參閱 支援的類型)
- ExpandoObject
- TimeZoneInfo
- BigInteger
- DBNull
- Type
- ValueTuple 及與其相關聯的泛型型別
自訂轉換器可以針對沒有內建支援的類型進行實作。
多型序列化
Newtonsoft.Json
會自動執行多型序列化。 自 .NET 7 起,System.Text.Json 支援透過 JsonDerivedTypeAttribute 屬性進行多型序列化。 如需詳細資訊,請參閱序列化衍生類別的屬性。
多型還原序列化
Newtonsoft.Json
具有 TypeNameHandling
設定,會在序列化時將類型名稱中繼資料新增至 JSON。 其會在還原序列化時,使用中繼資料執行多型還原序列化。 自 .NET 7 起,System.Text.Json 依賴類型鑑別子資訊來執行多型還原序列化。 此中繼資料會以 JSON 發出,然後在還原序列化期間用於判斷是否要還原序列化為基底類型或衍生類型。 如需詳細資訊,請參閱序列化衍生類別的屬性。
若要支援多型還原序列化,請建立如同如何撰寫自訂轉換器中範例的轉換器。
還原序列化字串列舉值
根據預設,System.Text.Json 不支援還原序列化字串列舉值,而 Newtonsoft.Json
則支援。 例如,下列程式碼會擲回 JsonException:
string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.
class MyObj
{
public string Text { get; set; } = "";
public MyEnum Enum { get; set; }
}
enum MyEnum
{
One,
Two,
Three
}
不過,您可以使用 JsonStringEnumConverter 轉換器來啟用字串列舉值的還原序列化。 如需詳細資訊,請參閱以字串表示列舉。
還原序列化物件屬性
當 Newtonsoft.Json
還原序列化為 Object 時,其會:
- 推斷 JSON 承載中基本值的類型 (非
null
),並以 Boxed 物件形式傳回預存string
、long
、double
、boolean
或DateTime
。 「基本值」為單一 JSON 值,例如 JSON 數字、字串、true
、false
或null
。 - 傳回
JObject
或JArray
,以用於 JSON 承載中的複雜值。 「複雜值」是大括號內的 JSON 機碼值組集合 ({}
),或括號中的值清單 ([]
)。 大括號或括號中的屬性與值可以有額外的屬性和值。 - 當承載具有
null
JSON 常值時,會傳回 Null 參考。
每當還原序列化為 System.Text.Json 時,JsonElement
會儲存基本值與複雜值的 Boxed Object,例如:
-
object
屬性。 -
object
字典值。 -
object
陣列值。 - 根
object
。
然而,當承載本身中有 System.Text.Json
JSON 常值時,null
會將 Newtonsoft.Json
視為與 null
相同,並傳回 Null 參考。
若要實作 object
屬性的型別推斷,請建立如同如何撰寫自訂轉換器中範例的轉換器。
將 Null 還原序列化為不可為 Null 的類型
Newtonsoft.Json
在下列案例中不會擲回例外狀況:
-
NullValueHandling
設定為Ignore
,且 - 在還原序列化期間,JSON 包含不可為 Null 之實值型別的 null 值。
在相同的案例中,System.Text.Json 會擲回例外狀況。 (System.Text.Json
中的對應 Null 處理設定為 JsonSerializerOptions.IgnoreNullValues = true
。)
若您擁有目標類型,則最佳因應措施是讓有問題的屬性變成可為 Null (例如,將 int
變更為 int?
)。
另一個因應措施是建立類型的轉換器,例如下列處理 DateTimeOffset
類型 null 值的範例:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
reader.TokenType == JsonTokenType.Null
? default
: reader.GetDateTimeOffset();
public override void Write(
Utf8JsonWriter writer,
DateTimeOffset dateTimeValue,
JsonSerializerOptions options) =>
writer.WriteStringValue(dateTimeValue);
}
}
使用屬性 (property) 上的屬性 (attribute),或將轉換器新增至 Converters 集合,來註冊此自訂轉換器。
注意:上述轉換器處理 null 值的方式與Newtonsoft.Json
處理用於指定預設值之 POCO 的方法不同。 例如,假設下列程式碼表示您的目標物件:
public class WeatherForecastWithDefault
{
public WeatherForecastWithDefault()
{
Date = DateTimeOffset.Parse("2001-01-01");
Summary = "No summary";
}
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
}
且假設下列 JSON 是使用上述轉換器來還原序列化:
{
"Date": null,
"TemperatureCelsius": 25,
"Summary": null
}
還原序列化之後,Date
屬性會有 1/1/0001 (default(DateTimeOffset)
),也就是覆寫建構函式中設定的值。 假設有相同的 POCO 與 JSON,Newtonsoft.Json
還原序列化會在 Date
屬性中保留 1/1/2001。
還原序列化為不可變類別和結構
因為 Newtonsoft.Json
可以使用具有參數的建構函式,所以可以還原序列化為不可變類別和結構。
在 System.Text.Json 中,使用 [JsonConstructor] 屬性來指定使用參數化建構函式。 C# 9 中的記錄也為不可變,且其支援作為還原序列化目標。 如需詳細資訊,請參閱不可變的類型與記錄。
必要屬性
在 Newtonsoft.Json
中,您可以藉由在 Required
屬性 (attribute) 上設定 [JsonProperty]
來指定屬性 (property) 為必要項目。 若 JSON 中未收到標記為必要屬性的值,則 Newtonsoft.Json
會擲回例外狀況。
自 .NET 7 起,您可以使用 C# required
修飾詞或 JsonRequiredAttribute 必要屬性 (property) 上的屬性 (attribute)。 若 JSON 承載未包含標記屬性的值,則 System.Text.Json 會擲回例外狀況。 如需詳細資訊,請參閱必要權限。
指定日期格式
Newtonsoft.Json
提供數種方式來控制 DateTime
與 DateTimeOffset
類型屬性的序列化及還原序列化方式:
- 此
DateTimeZoneHandling
設定可用於將所有DateTime
值序列化為 UTC 日期。 -
DateFormatString
設定與DateTime
轉換器可用於自訂日期字串的格式。
System.Text.Json 支援 ISO 8601-1:2019,包括 RFC 3339 設定檔。 此格式受到廣泛採用,是一個明確的格式,可精確地進行來回行程。 若要使用任何其他格式,請建立自訂轉換器。 例如,下列轉換器會將使用包含或不包含時區位移之 UNIX Epoch 格式的 JSON 序列化及還原序列化 (例如 /Date(1590863400000-0700)/
或 /Date(1590863400000)/
等值):
sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);
if (
!match.Success
|| !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
|| !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
|| !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
{
throw new JsonException();
}
int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);
return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
}
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
TimeSpan utcOffset = value.Offset;
string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");
writer.WriteStringValue(formatted);
}
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);
if (
!match.Success
|| !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
{
throw new JsonException();
}
return s_epoch.AddMilliseconds(unixTime);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
writer.WriteStringValue(formatted);
}
}
如需詳細資訊,請參閱 System.Text.Json 中的 DateTime 與 DateTimeOffset 支援。
回撥
Newtonsoft.Json
可讓您在序列化或還原序列化流程中的數個時間點執行自訂程式碼:
- OnDeserializing (開始將物件還原序列化時)
- OnDeserialized (完成將物件還原序列化時)
- OnSerializing (開始將物件序列化時)
- OnSerialized (完成將物件序列化時)
System.Text.Json 會在序列化與還原序列化期間公開相同的通知。 若要使用這些通知,請從 System.Text.Json.Serialization 命名空間實作下列一或多個介面:
下列範例會檢查 null 屬性,並在序列化與還原序列化的開頭與結尾撰寫訊息:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Callbacks
{
public class WeatherForecast :
IJsonOnDeserializing, IJsonOnDeserialized,
IJsonOnSerializing, IJsonOnSerialized
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
void IJsonOnDeserializing.OnDeserializing() => Console.WriteLine("\nBegin deserializing");
void IJsonOnDeserialized.OnDeserialized()
{
Validate();
Console.WriteLine("Finished deserializing");
}
void IJsonOnSerializing.OnSerializing()
{
Console.WriteLine("Begin serializing");
Validate();
}
void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished serializing");
private void Validate()
{
if (Summary is null)
{
Console.WriteLine("The 'Summary' property is 'null'.");
}
}
}
public class Program
{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
};
string jsonString = JsonSerializer.Serialize(weatherForecast);
Console.WriteLine(jsonString);
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
Console.WriteLine($"Date={weatherForecast?.Date}");
Console.WriteLine($"TemperatureCelsius={weatherForecast?.TemperatureCelsius}");
Console.WriteLine($"Summary={weatherForecast?.Summary}");
}
}
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}
//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=
OnDeserializing
程式碼無法存取新的 POCO 執行個體。 若要在還原序列化開始時操作新的 POCO 執行個體,請將該程式碼放在 POCO 建構函式中。
非公用屬性 setter 與 getter
Newtonsoft.Json
可以透過 JsonProperty
屬性 (attribute) 使用私用與內部屬性 (property) setter 與 getter。
System.Text.Json 可以透過 [JsonInclude] 屬性 (attribute) 支援私用與內部屬性 (property) setter 與 getter。 如需範例程式碼,請參閱非公用屬性存取子。
填入現有的物件
JsonConvert.PopulateObject
中的 Newtonsoft.Json
方法會將 JSON 文件還原序列化為類別的現有執行個體,而不是建立新的執行個體。
System.Text.Json 一律使用預設公用無參數建構函式來建立目標類型的新執行個體。 自訂轉換器可還原序列化為現有的執行個體。
重複使用而非取代屬性
從 .NET 8 開始,System.Text.Json 支援重複使用初始化的屬性,而不是取代它們。 行為具有一些差異,您可以在 API 提案中閱讀。
如需詳細資訊,請參閱填入初始化的屬性。
在沒有 setter 的情況下填入屬性
從 .NET 8 開始,System.Text.Json 支援填入屬性,包括沒有 setter 的屬性。 如需詳細資訊,請參閱填入初始化的屬性。
蛇形命名法原則
System.Text.Json 包含蛇形蛇形命名法的內建命名原則。 不過,針對某些輸入與 Newtonsoft.Json
具有行為差異。 下表顯示使用 JsonNamingPolicy.SnakeCaseLower 原則轉換輸入時的一些差異。
輸入 | Newtonsoft.Json 結果 | System.Text.Json 結果 |
---|---|---|
"AB1" | "a_b1" | "ab1" |
"SHA512Managed" | "sh_a512_managed" | "sha512_managed" |
"abc123DEF456" | "abc123_de_f456" | "abc123_def456" |
"KEBAB-CASE" | "keba_b-_case" | "kebab-case" |
System.Runtime.Serialization 屬性
System.Runtime.Serialization 屬性,例如 DataContractAttribute、DataMemberAttribute 和 IgnoreDataMemberAttribute,可讓您定義資料合約。 「 資料合約 」(Data Contract) 是服務與用戶端之間的正式合約,其中會抽象地描述要交換的資料。 資料合約會精確地定義要序列化哪些屬性以進行交換。
System.Text.Json 沒有這些屬性的內建支援。 不過,從 .NET 7 開始,您可以使用自訂類型解析程式來新增支援。 如需範例,請參閱 ZCS.DataContractResolver。
八進位數字
Newtonsoft.Json
會將前置字元為零的數字視為八進位數字。 因為 System.Text.Json 規格不允許前置字元為零,所以 不允許前置字元為零。
處理遺漏的成員
如果要還原序列化的 JSON 包含目標類型中遺漏的屬性,則 Newtonsoft.Json
可以設定為擲回例外狀況。 根據預設,除非您使用 System.Text.Json,否則 會忽略 JSON 中的額外屬性。
在 .NET 8 和更新版本中,您可以使用下列其中一種方法來設定是否跳過或不允許未對應 JSON 屬性的喜好設定:
- 將 JsonUnmappedMemberHandlingAttribute 屬性套用至您要還原序列化的類型。
- 若要全域設定您的喜好設定,請設定 JsonSerializerOptions.UnmappedMemberHandling 屬性。 或者,針對來源產生,請設定 JsonSourceGenerationOptionsAttribute.UnmappedMemberHandling 屬性,並將屬性套用至您的 JsonSerializerContext 類別。
- 自訂 JsonTypeInfo.UnmappedMemberHandling 屬性。
JsonObjectAttribute
Newtonsoft.Json
具有可在類型層級JsonObjectAttribute
套用的屬性 ,以控制哪些成員已序列化、如何處理 null
值,以及是否需要所有成員。
System.Text.Json 沒有可在類型上套用的對等屬性。 對於某些行為,例如 null
值處理,您可以在全域 JsonSerializerOptions 或每個屬性上個別設定相同的行為。
請考慮使用 Newtonsoft.Json.JsonObjectAttribute
來指定應忽略所有 null
屬性的下列範例:
[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }
在 System.Text.Json 中,您可以設定所有類型和屬性的行為:
JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string json = JsonSerializer.Serialize<Person>(person, options);
或者,您可以個別設定每個屬性的行為:
public class Person
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Name { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? Age { get; set; }
}
接下來,請考慮下列使用 Newtonsoft.Json.JsonObjectAttribute
來指定 JSON 中必須存在所有成員屬性的範例:
[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }
您可以將 C# System.Text.Json 修飾元或 required
新增至每個屬性JsonRequiredAttribute,以達到 中相同的行為。 如需詳細資訊,請參閱必要權限。
public class Person
{
[JsonRequired]
public string? Name { get; set; }
public required int? Age { get; set; }
}
TraceWriter
Newtonsoft.Json
可讓您使用 TraceWriter
進行偵錯,以檢視序列化或還原序列化所產生的記錄。
System.Text.Json 不會執行記錄。
JsonDocument 及 JsonElement 與 JToken (例如 JObject、JArray) 的比較
System.Text.Json.JsonDocument 提供從現有 JSON 承載剖析及建置唯讀文件物件模組 (DOM) 的能力。 DOM 提供 JSON 承載中的資料隨機存取。 撰寫承載的 JSON 元素可以透過 JsonElement 類型存取。 此 JsonElement
類型提供 API,可將 JSON 文字轉換成一般 .NET 類型。
JsonDocument
會公開 RootElement 屬性。
自 .NET 6 起,您可以使用 命名空間中的 JsonNode 類型與其他類型,從現有的 JSON 承載剖析及建置System.Text.Json.Nodes DOM。 如需詳細資訊,請參閱使用 JsonNode
。
JsonDocument 為 IDisposable
JsonDocument
會將資料的記憶體內部檢視建置到集區緩衝區中。 因此,JObject
類型不同於 JArray
的 Newtonsoft.Json
或 JsonDocument
,其會實作 IDisposable
,且必須用於 using 區塊中。 如需詳細資訊,請參閱 JsonDocument 為 IDisposable。
JsonDocument 為唯讀
System.Text.Json DOM 無法新增、移除或修改 JSON 元素。 此設計方式是為了取得效能,並減少剖析常見 JSON 承載大小的配置 (也就是 < 1 MB)。
JsonElement 為聯集 struct
JsonDocument
會將 RootElement
公開為類型 JsonElement 的屬性,這是包含任何 JSON 元素的聯集結構類型。
Newtonsoft.Json
會使用專用階層式類型,例如 JObject
、JArray
、JToken
等等。
JsonElement
是您可以搜尋及列舉的內容,且您可以使用 JsonElement
將 JSON 元素具體化為 .NET 類型。
自 .NET 6 起,您可以使用 JsonNode 類型,以及對應至 System.Text.Json.Nodes、JObject
與 JArray
之 JToken
命名空間中的類型。 如需詳細資訊,請參閱使用 JsonNode
。
如何搜尋 JsonDocument 與 JsonElement 以尋找子元素
因為 JObject
中的 JArray
或 Newtonsoft.Json
在某些字典中為查閱,所以將其用於搜尋 JSON 權杖會相對快速。 相較之下,因為 JsonElement
搜尋需要循序搜尋屬性,所以相對緩慢 (例如使用 TryGetProperty
時)。
System.Text.Json 的設計目的是將初始剖析時間降至最低,而不是查閱時間。 如需詳細資訊,請參閱如何搜尋 JsonDocument 與 JsonElement 以尋找子元素。
Utf8JsonReader 與JsonTextReader
System.Text.Json.Utf8JsonReader 是一個高效能、低配置且順向的 UTF-8 編碼 JSON 文字,並會從 ReadOnlySpan<byte> 或 ReadOnlySequence<byte> 讀取。
Utf8JsonReader
是一個低階類型,可用於建置自訂剖析器與還原序列化程式。
Utf8JsonReader 為 ref struct
JsonTextReader
中的 Newtonsoft.Json
是類別。
Utf8JsonReader
類型的差異在於其為「參考結構」。 如需詳細資訊,請參閱 utf8JsonReader 的 ref 結構限制。
將 null 值讀入可為 Null 的實值型別
Newtonsoft.Json
會提供可傳回 Nullable<T> 的 API (例如 ReadAsBoolean
),其藉由傳回 Null
來為您處理 TokenType
bool?
。 內建 System.Text.Json
API 只會傳回不可為 Null 的實值型別。 如需詳細資訊,請參閱將 null 值讀入可為 Null 的實值型別。
用於讀取 JSON 的多目標
若必須繼續針對特定目標 Framework 來使用 Newtonsoft.Json
,您可使用多個目標並擁有兩個實作。 然而,這並不簡單,且需要一些 #ifdefs
與來源複製。 盡可能多共用程式碼的其中一種方式,即建立 ref struct
包裝函式來含括 Utf8JsonReader 與 Newtonsoft.Json.JsonTextReader
。 此包裝函式會整合公用介面區,同時隔離行為差異。 這可讓您隔離類型建構的大部分變更,以及使用傳址方式傳遞新類型。 這是 Microsoft.Extensions.DependencyModel (英文) 程式庫遵循的模式:
Utf8JsonWriter 與JsonTextWriter
System.Text.Json.Utf8JsonWriter 是一種高效能方式,可從常見的 .NET 類型 (例如 String
、Int32
與 DateTime
) 撰寫 UTF-8 編碼的 JSON 文字。 寫入器是一個低階類型,可用於建置自訂序列化程式。
撰寫原始值
Newtonsoft.Json
的 WriteRawValue
方法會撰寫具有預期值的原始 JSON。
System.Text.Json 具有直接對等項目:Utf8JsonWriter.WriteRawValue。 如需詳細資訊,請參閱撰寫原始 JSON。
自訂 JSON 格式
JsonTextWriter
包含下列設定,其中 Utf8JsonWriter 沒有對應項:
-
QuoteChar (英文) - 指定要用於括住字串值的字元。
Utf8JsonWriter
一律使用雙引號。 -
QuoteName (英文) - 指定是否要以引號括住屬性名稱。
Utf8JsonWriter
一律使用引號括住。
從 .NET 9 開始,您可以自訂縮排字元和大小,讓 Utf8JsonWriter 使用 JsonWriterOptions 結構公開的選項:
JsonTextWriter
包含下列設定,其中 Utf8JsonWriter
沒有對應項:
-
Indentation (英文) - 指定要縮排的字元數。
Utf8JsonWriter
一律縮排 2 個字元。 -
IndentChar (英文) - 指定要用於縮排的字元。
Utf8JsonWriter
一律使用空白字元。 -
QuoteChar (英文) - 指定要用於括住字串值的字元。
Utf8JsonWriter
一律使用雙引號。 -
QuoteName (英文) - 指定是否要以引號括住屬性名稱。
Utf8JsonWriter
一律使用引號括住。
沒有任何因應措施可讓您透過這些方式自訂 Utf8JsonWriter
所產生的 JSON。
撰寫 Timespan、URI 或 char 值
JsonTextWriter
提供 WriteValue
(英文)、URI (英文) 與 char (英文) 值的 方法。
Utf8JsonWriter
沒有對等的方法。 作為替代,請將這些值格式化為字串 (例如呼叫 ToString()
),並呼叫 WriteStringValue。
用於寫入 JSON 的多目標
若必須繼續針對特定目標 Framework 來使用 Newtonsoft.Json
,您可使用多個目標並擁有兩個實作。 然而,這並不簡單,且需要一些 #ifdefs
與來源複製。 盡可能多共用程式碼的其中一種方式,即建立包裝函式來含括 Utf8JsonWriter 與 Newtonsoft.Json.JsonTextWriter
。 此包裝函式會整合公用介面區,同時隔離行為差異。 這可讓您隔離類型建構的大部分變更。
Microsoft.Extensions.DependencyModel (英文) 程式庫如下:
不支援 TypeNameHandling.All
從 TypeNameHandling.All
中排除 System.Text.Json
對等功能的決定是刻意為之。 允許 JSON 承載來指定自己的類型資訊,是 Web 應用程式中常見的弱點來源。 具體來說,使用 Newtonsoft.Json
設定 TypeNameHandling.All
可讓遠端用戶端在 JSON 承載本身內嵌整個可執行應用程式,以便在還原序列化期間,讓 Web 應用程式擷取並執行內嵌程式碼。 如需詳細資訊,請參閱 Friday the 13th JSON attacks PowerPoint 與 Friday the 13th JSON attacks details。
不支援 JSON 路徑查詢
JsonDocument
DOM 不支援使用 JSON 路徑 (英文) 進行查詢。
在 JsonNode DOM 中,每個 JsonNode
執行個體都有一個傳回該節點路徑的 GetPath
方法。 但沒有任何內建 API 可處理以 JSON 路徑查詢字串為基礎的查詢。
如需詳細資訊,請參閱 dotnet/runtime #31068 GitHub 問題 (英文)。
部分限制無法設定
System.Text.Json 會設定部分值無法變更的限制,例如字元中 (166 MB) 與 Base 64 中 (125 MB) 的權杖大小上限。 如需詳細資訊,請參閱原始程式碼中的 JsonConstants
與 GitHub 問題 dotnet/runtime #39953 (英文)。
NAN、Infinity、-Infinity
Newtonsoft 會剖析 NaN
、Infinity
與 -Infinity
JSON 字串權杖。 搭配 System.Text.Json 使用 JsonNumberHandling.AllowNamedFloatingPointLiterals。 如需如何使用此設定的資訊,請參閱允許或撰寫引號中的數字。
使用 GitHub Copilot 移轉
您可以從 GitHub Copilot 取得程式碼撰寫說明,以在 IDE 中轉換程式碼。
如果您使用的是 Visual Studio 2022 版本 17.8 或更新版本,您可以嘗試在 Visual Studio 中使用由 AI 驅動的 GitHub Copilot,以協助將 Newtonsoft.Json
程式碼移轉到使用 System.Text.Json
。 將您的問題和要轉換的程式碼提交到 Copilot 聊天視窗中,範例如下:
注意
GitHub Copilot 是由 AI 驅動的,因此可能會有意外和錯誤的情況發生。 任何生成的代碼或建議都務必要驗證。 有關 GitHub Copilot 的一般使用、產品影響、人為監督和隱私權的更多資訊,請參閱 [GitHub Copilot 常見問題解答]。
Copilot Chat 的範例提示
convert the following code to use System.Text.Json
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);
Copilot Chat 回應的範例
To convert the code to use System.Text.Json, you can use the JsonSerializer class. Here's the updated code:
using System;
using System.Text.Json;
public class Product
{
public string Name { get; set; }
public DateTime ExpiryDate { get; set; }
public decimal Price { get; set; }
public string[] Sizes { get; set; }
}
public class Program
{
public static void Main()
{
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonSerializer.Serialize(product);
Console.WriteLine(output);
}
}
This code uses System.Text.Json.JsonSerializer.Serialize to convert the Product object to a JSON string.
請注意,您的結果可能會與這些範例回應中顯示的內容有所不同。 AI 模型不具決定性,這代表在詢問相同問題時,它們可能會傳回不同的回答。 這可能是由於隨著時間而進行的額外學習和調整、語言變化、上下文變化 (例如您的聊天記錄) 等因素造成的。
您可以使用聊天功能,如 [斜線命令、參考和執行緒],來設定意圖,並獲得具有範圍上下文的更佳答案。
例如,如果您的程式碼檔案 filename
在 IDE 中打開,您可以在提示中使用「轉換 以使用 #filename
」來System.Text.Json
,以便在 Copilot Chat 中進行操作。 或者,您可以在聊天視窗中或內嵌聊天中,使用「轉換 以使用 @workspace
」來System.Text.Json
。