System.Text.Json 自定义 Conveter
System.Text.Json 自定義 Conveter
Intro
System.Text.Json 作為現在 .NET 默認提供的高性能 JSON 序列化器,對于一些比較特殊類型支持的并不太好,業務需求中總是有各種各樣的需要,很多時候就需要用到自定義 Converter ,對于微軟新出的 DateOnly/TimeOnly 也是需要自定義 Converter 來支持的
Sample
遇到一個(偽)需求,一個 Id 屬性可能是字符串也可能是整型數字,舉個栗子,
{"Id":?1,?"Name":?"Test"}{"Id":?"這是一個?Id",?"Name":?"Test"}上面這是兩個 JSON,想實現用同一個 Model 來保存結果,應該怎么做呢?
如果 Id 只會是整數或者整數的字符串,那么我們就可以用 int 來表示,System.Text.Json 從 5.0 開始支持解析帶引號的數字,也就是數字的字符串形式可以參考:https://github.com/dotnet/runtime/issues/30255,只需要配置 JsonNumberHandling, 在 ASP.NET Core 中默認是啟用的,是可以把 "1" 反序列化成一個 int 類型的
但是我們的示例中的 Id 是可能不是數字的,轉成數字可能會失敗的,所以想要把它當作 string 來處理,最后 model 是這樣的
public?record?TestModel {public?string?Id?{?get;?init;?}?=?default!;public?string??Name?{?get;?set;?} }但是如果是上面第一種形式的 JSON 反序列化時會發生錯誤,異常如下:
所以還需要自定義一個 Converter 來支持將數字轉換成一個字符串,Converter 實現如下,? 屬性類型是什么,泛型類型就應該是什么
public?class?StringOrIntConverter?:?JsonConverter<string> {public?override?string?Read(ref?Utf8JsonReader?reader,?Type?typeToConvert,?JsonSerializerOptions?options){if?(reader.TokenType?==?JsonTokenType.Number){return?reader.GetInt32().ToString();}return?reader.GetString();}public?override?void?Write(Utf8JsonWriter?writer,?string?value,?JsonSerializerOptions?options){writer.WriteStringValue(value);} }使用 Converter 的方式有兩種,一種是在某個屬性上添加 JsonConverter 來使用,另一種是作為全局 Converter 來使用,直接配置 JsonSerializerOptions 中的 Converter
屬性使用 Converter 示例:
public?record?TestModel {[JsonConverter(typeof(StringOrIntConverter))]public?string?Id?{?get;?init;?}?=?default!;public?string??Name?{?get;?set;?} }配置 JsonSerializerOptions示例:
JsonSerializer.Deserialize<TestModel>(node.ToJsonString(),?new?JsonSerializerOptions{Converters?={new?StringOrIntConverter()}});這樣我們就可以支持從一個 int 到 string 的轉換了,完整示例如下:
var?model?=?new?TestModel {Id?=?"123",Name?=?"456" }; var?jsonString?=?JsonSerializer.Serialize(model); WriteLine(jsonString); var?node?=?JsonNode.Parse(jsonString); ArgumentNullException.ThrowIfNull(node,?nameof(node)); node["Id"]?=?123; var?newJsonString?=?node.ToJsonString(); WriteLine(newJsonString); var?newModel?=?JsonSerializer.Deserialize<TestModel>(newJsonString); WriteLine(model?==?newModel);node["Name"]?=?345; WriteLine(JsonSerializer.Deserialize<TestModel>(node.ToJsonString(),?new?JsonSerializerOptions {Converters?={new?StringOrIntConverter()} })?.Name);輸出結果如下:
outputMore
可能你會問為什么不直接用 object,如果使用 object 的話,上面的 Equals 判斷就要改寫了,需要自己重新實現比較邏輯,而用 string 就不需要了 希望上面自定義 Converter 的代碼對你有所幫助~
References
https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?WT.mc_id=DT-MVP-5004222
https://github.com/dotnet/runtime/issues/30255
https://github.com/dotnet/runtime/pull/39685
https://github.com/WeihanLi/SamplesInPractice/blob/master/JsonSample/SystemTextJsonSample/CustomConvertSample.cs
總結
以上是生活随笔為你收集整理的System.Text.Json 自定义 Conveter的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 拥抱开源!除了微软红帽,这些国际大厂你认
- 下一篇: 为什么 Dapper 的批量插入比我预期