從 Newtonsoft.Json 遷移到 System.Text.Json
一.寫在前面
System.Text.Json 是 .NET Core 3 及以上版本內置的 Json 序列化組件,剛推出的時候經常看到踩各種坑的吐槽,現在經過幾個版本的迭代優化,提升了易用性,修復了各種問題,是時候考慮使用 System.Text.Json 了。本文將從使用層面來進行對比。
System.Text.Json 在默認情況下十分嚴格,避免進行任何猜測或解釋,強調確定性行為。比如:字符串默認轉義,默認不允許尾隨逗號,默認不允許帶引號的數字等,不允許單引號或者不帶引號的屬性名稱和字符串值。 該庫是為了實現性能和安全性而特意這樣設計的。Newtonsoft.Json
默認情況下十分靈活。
關於性能,參考 Incerry 的性能測試:.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json ,如果打算使用 .NET 7 不妨考慮一下 System.Text.Json。
Newtonsoft.Json 使用 13.0.2 版本,基於 .NET 7。
兩者對比着使用,請看 https://www.cnblogs.com/stulzq/p/17118904.html
命名空間
System.Text.Json:默認情況下,System.Text.Json使用運行時反射來收集它需要訪問序列化和反序列化對象的屬性的元數據。作為一種替代方法,System.Text.Json 6.0可以使用c#源生成器特性來提高性能,減少私有內存的使用,並簡化程序組裝,從而減少應用程序大小。例如在asp.net中可以讀取 XML、JSON 直接生成 C# 代碼參與編譯,DTO 編寫全自動化都是沒問題的。編譯時反射 - 0 運行時開銷。
.NET 6 中的 System.Text.Json 已做了許多改進,因此它現在是一個“工業強度”的序列化解決方案。
源生成器
.NET 6 為 添加了新的源生成器。 源生成可與 JsonSerializer 配合使用,並且可以通過多種方式進行配置。 它可以提高性能、減少內存使用量並便於程序集剪裁。 有關詳細信息,請參閱如何在 System.Text.Json 中選擇反射或源生成以及如何在 System.Text.Json 中使用源生成。
System.Text.Json.Nodes
System.Text.Json.Serialization
JSON簡介
JSON 是一種數據格式,已成為 XML 的流行替代品。它簡單而整潔,語法類似於 JavaScript 對象。實際上,術語 JSON 代表 JavaScript Object Notation。.NET 的最新版本為處理 JSON 數據提供了內置支持。
System.Text.Json 命名空間提供高性能、低分配的功能來處理 JSON 數據。這些功能包括將對象序列化為 JSON 並將 JSON 反序列化為對象。它還提供用於創建內存中文檔對象模型 (DOM) 的類型,用於訪問 JSON 文檔中的任何元素,提供數據的結構化視圖。
支持 System.Runtime.Serialization
特性。System.Text.Json
中不支持 System.Runtime.Serialization 命名空間中的特性。
詳細請查看 【C# 序列化】 JSON-在.net croe中的應用
Text.Json支持的格式
JSON 數據的書寫格式是:名稱/值對,數據由逗號分隔。.net core 對json 的處理默認使用駝峰式,
JSON 值可以是:
數字(整數或浮點數)
字符串(在雙引號中)
邏輯值(true 或 false)
數組(在方括號中)[,]
對象(在花括號中){}
null
public class Serializeables { public DateTimeOffset Date { get; set; } public int TemperatureCelsius { get; set; } public string Ser = "first ser"; public bool Sers = true; public bool[] Serbool = { true ,true,true}; public List<SerrializePeople> SerList = new(); } //輸出 /*{ "Date": "0001-01-01T00:00:00+00:00", "TemperatureCelsius": 0, "Ser": "first ser", "Sers": true, "Serbool": [true, true, true], "SerList": [{ "Person": null }, { "Person": null }] } */
Json序列化器 簡介
功能
了具有以下功能的JsonSerializer
:
- 支持對簡單傳統CLR對象(POCO)、基元類型、集合進行序列化和反序列化;
- 內置的異步序列化和反序列化;
- 原生處理UTF-8數據;
- 反序列化可選是否區分大小寫;
- 使用
JsonNamingPolicy.CamelCase
的駝峰命名策略; - 使用
JsonNamingPolicy
指定自定義命名策略; - 轉義的JSON數據反序列化;
- 序列化時可選的最小字符轉義;
- 序列化時忽略空值;
- 反序列化時忽略注釋;
- 允許(JSON)尾隨逗號;
- 可定制轉換器;
- 使用
[JsonExtensionData]
特性指示在反序列化時沒有匹配成員的屬性存放處(a property overflow bag);(注:當屬性的類型為IDictionary<TKey,TValue>
時,沒有匹配成員的任何屬性都會在反序列化期間添加到該字典中,並在序列化期間中寫入) - 使用
[JsonIgnore]
特性忽略特定的屬性; - 使用
[JsonProperty]
特性來指定自定義屬性名。(注:未使用[JsonProperty]
特性時Json對應的實體類屬性名需要與Json對應屬性完全一致,使用[JsonProperty]
特性標識PropertyName
后,實體類屬性名可自定義)
序列化選項設置
- 忽略屬性
- 忽略單個屬性,使用[JsonIgnore] 特性和JsonIgnoreCondition枚舉類忽略當個屬性
- 忽略所有只讀屬性
- 忽略所有 null 值屬性
- 忽略所有默認值屬性
- 允許無效的 JSON
- 處理溢出 JSON,使用 JsonElement 或 JsonNode
- 保留引用,處理循環引用
- 保留引用並處理循環引用 將元數據屬性寫入 (
$id
、$values
和$ref
)ReferenceHandler = ReferenceHandler.Preserve,
- 跨多個序列化和反序列化調用保留引用元數據(不理解,趕緊用不到)
- 忽略循環引用
ReferenceHandler = ReferenceHandler.IgnoreCycles,
- 保留引用並處理循環引用 將元數據屬性寫入 (
- 反序列化為不可變類型,非公共訪問器
- 多態序列化 本文介紹的是序列化,而不是反序列化。 不支持多態反序列化,解決方法是,你編寫一個自定義轉換器,例如支持多態反序列化中的示例。不過已經不需要寫反序列化類型,.net6.0可以直接使用Writable Dom。
- 反序列化 字符字符編碼問題
序列化控制
Text.Json新增了4個接口
IJsonOnDeserialized代替[OnSerialized]
IJsonOnDeserializing代替[OnSerializing]
IJsonOnSerialized代替[OnDeserialized]
IJsonOnSerializing代替[OnDeserializing]
IJsonOnDeserializing, IJsonOnDeserialized, IJsonOnSerializing, IJsonOnSerialized public class Serializeables : IJsonOnDeserializing, IJsonOnDeserialized,IJsonOnSerializing, IJsonOnSerialized { public bool[] Serbool = { true, true, true }; public void OnSerializing() => Console.WriteLine("序列化中"); public void OnDeserializing() => Console.WriteLine("反序列化中"); public void OnDeserialized() => Console.WriteLine("反序列化后"); public void OnSerialized() => Console.WriteLine("序列化后"); }
[JsonIgnore] 特性 忽略屬性
“序列化通知”功能對於設置默認值和驗證屬性值合法性非常有用。
JsonIgnoreCondition Enum
Always 1 屬性將始終被忽略。
Never 0 屬性將始終被序列化和反序列化,無論IgnoreNullValues配置如何。無論 DefaultIgnoreCondition
、IgnoreReadOnlyProperties
和 IgnoreReadOnlyFields
全局設置如何,始終序列化和反序列化屬性。
WhenWritingDefault 2 屬性只有在為空時才會被忽略。如果屬性是引用類型 null
可為 null 的值類型 null
或值類型 default
,則在序列化中忽略屬性。
WhenWritingNull 3 如果該值為空,則在序列化期間忽略該屬性。這只適用於引用類型的屬性和字段。WhenWritingNull
- 如果屬性是引用類型 null
或可為 null 的值類型 null
,則在序列化中忽略屬性。
public class MySerializeable { [JsonIgnore(Condition =JsonIgnoreCondition.Always)] public string Name = "dfdf";//被忽略 [JsonIgnore(Condition =JsonIgnoreCondition.Never)] public string Name2 = "dfdf"; [JsonIgnore(Condition =JsonIgnoreCondition.WhenWritingDefault)] public string Name3 = default;//將被忽略,因為使用默認值 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Name4 =null;//會被忽略因為null }
綜合使用案例
using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.Unicode; JsonSerializerOptions jso = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, ReadCommentHandling = JsonCommentHandling.Skip, //跳過注釋 AllowTrailingCommas = true,//忽略逗號 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,//屬性名的格式,駝峰 NumberHandling = JsonNumberHandling.AllowReadingFromString,//反序列化的時候用的到 IncludeFields=true, //保證中文字符正確顯示。CjkUnifiedIdeographs 象行文字 代表中文(Chinese)、日文(Japanese )、韓文(Korean)的字符集合 Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs),//從拉丁文字到象行文字 }; var json=JsonSerializer.SerializeToNode(new Myserliazer("祖海", "Summary", "Summary", "Summary"),jso); Console.WriteLine(json.ToJsonString(options: jso)); public class Myserliazer { public Myserliazer() { } [JsonConstructor] public Myserliazer(string name, string sumary, string description, string keyword) => (Name, Summary, Description, KeyWord) = (name, sumary, description, keyword); //[JsonInclude] 用法 [JsonInclude] public string Name = "董卿"; [JsonInclude]//若要允許使用非公共屬性訪問器,請使用 [JsonInclude] 特性 public string? Summary { private get; set; } = "Summary"; [JsonInclude] public string Content { get; private set; } = @"[OnSerialized()] internal void OnSerializedMethod(StreamingContext context) ... the call to the onSerializing method when the serialized objects are equal."; public string Frend = "哈哈"; //[JsonIgnore]用法 public string Description { get; set; } = "My first int"; [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public string KeyWord { get; set; } = "Web Site"; [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public DateTime DateTimenow { get; set; } = DateTime.Now; public string Eeditor{ get; } // [JsonExtensionData]用法 [JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; } = new Dictionary<string, JsonElement>(); // [JsonPropertyName]用法 [JsonPropertyName("Cangpr0pety")] public string ChangPr0pety { set; get; } = "ChangPr0pety"; public int Age { set; get; } = 12; public void OnDeserializing() { throw new NotImplementedException(); } }