【C# 序列化】 自定義Json轉換器
Json轉換器簡介
本文介紹如何為 System.Text.Json 命名空間中提供的 JSON 序列化類創建自定義轉換器。
轉換器是一種將對象或值與 JSON 相互轉換的類。 System.Text.Json
命名空間為映射到 JavaScript 基元的大多數基元類型提供內置轉換器。 可以編寫自定義轉換器來實現以下目標:
- 重寫內置轉換器的默認行為。 例如,你可能希望通過 mm/dd/yyyy 格式來表示
DateTime
值。 默認情況下,支持 ISO 8601-1:2019,包括 RFC 3339 配置文件。 有關詳細信息,請參閱 中的 DateTime 和 DateTimeOffset 支持。 - 支持自定義值類型。 例如,
PhoneNumber
結構。
還可以編寫自定義轉換器,以使用當前版本中未包含的功能自定義或擴展 System.Text.Json
。 本文后面部分介紹了以下方案:
自定義轉換器模式(基本模式和工廠模式)
用於創建自定義轉換器的模式有兩種:基本模式和工廠模式。 工廠模式適用於處理類型 Enum
或開放式泛型的轉換器。 基本模式適用於非泛型或封閉式泛型類型。 例如,適用於以下類型的轉換器需要工廠模式:
可以通過基本模式處理的類型的一些示例包括:
基本模式創建的類可以處理一種類型。 工廠模式創建的類在運行時確定所需的特定類型,並動態創建適當的轉換器。
自定義基本模式轉換器
遵循基本模式的步驟
以下步驟說明如何遵循基本模式來創建轉換器:
- 創建一個派生自 JsonConverter<T> 的類,其中
T
是要進行序列化和反序列化的類型。 - 重寫
Read
方法,以反序列化傳入 JSON 並將其轉換為類型T
。 使用傳遞給方法的 Utf8JsonReader 讀取 JSON。 無需擔心處理部分數據,因為序列化程序會傳遞當前 JSON 范圍的所有數據。 因此,不需要調用 Skip 或 TrySkip,也不需要驗證 Read 是否返回true
。 - 重寫
Write
方法以序列化T
類型的傳入對象。 使用傳遞給方法的 Utf8JsonWriter 寫入 JSON。 - 僅當需要時才重寫
CanConvert
方法。 當要轉換的類型屬於類型T
時,默認實現會返回true
。 因此,僅支持類型T
的轉換器不需要重寫此方法。 有關的確需要重寫此方法的轉換器的示例,請參閱本文后面的多態反序列化部分。
可以參閱內置轉換器源代碼作為用於編寫自定義轉換器的參考實現。
屬性上的 [JsonConverter]
- 將轉換器類的實例添加到 JsonSerializerOptions.Converters 集合中。
- 將 [JsonConverter] 特性應用於需要自定義轉換器的屬性
JsonSerializerOptions op = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, Converters = { new MYEnumToUpperStingConverter() },//將轉換器類的實例添加到 JsonSerializerOptions.Converters 集合中。 WriteIndented = true, }; WeatherForecastWithEnum wf=new WeatherForecastWithEnum(); string wfw= JsonSerializer.Serialize(wf, op); WeatherForecastWithEnum wff=JsonSerializer.Deserialize<WeatherForecastWithEnum>(wfw,op); Console.WriteLine( wfw); public class MYEnumToUpperStingConverter : JsonConverter<Summary> { public override Summary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return (Summary)Enum.Parse(typeToConvert, reader.GetString()); } public override void Write(Utf8JsonWriter writer, Summary value, JsonSerializerOptions options) { writer.WriteStringValue( Enum.GetName(value)); } } public class WeatherForecastWithEnum { public DateTimeOffset Date { get; set; } = DateTime.Now; public int TemperatureCelsius { get; set; } = new Random().Next(1000); [JsonConverter(typeof(MYEnumToUpperStingConverter))]//將 [JsonConverter] 特性應用於需要自定義轉換器的屬性。 public Summary Summaryp { get; set; } = Summary.Cold; [JsonConverter(typeof(MYEnumToUpperStingConverter))] public Summary Summaryp2 { get; set; } = Summary.Cold; } public enum Summary { Cold, Cool, Warm, Hot }
類型上的 [JsonConverter]
- 將轉換器類的實例添加到 JsonSerializerOptions.Converters 集合中。
- 將 [JsonConverter] 屬性應用於表示自定義值類型的類或結構。
using System.Text.Json; using System.Text.Json.Serialization; namespace SystemTextJsonSamples; public class test { static void Main() { Weather weather = new Weather(); JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = true, }; string tem= JsonSerializer.Serialize(weather, options); Console.WriteLine(tem); Weather weather2 = JsonSerializer.Deserialize<Weather>(tem); Console.Read(); } } /* 輸出 { "Temperatur": "23C" }*/ //解析 之所能輸出這個結果 是因為序列化和反序列化時,類型的[JsonConverter(typeof(TemperatureConverter))] 特性在起作用
如果類型不加特性[JsonConverter(typeof(TemperatureConverter))] 將輸出以下結果:
{
"Temperatur": {
"Degrees": 23,
"IsCelsius": true,
"IsFahrenheit": false
}
}
public class Weather { public Temperature Temperatur { get; set; } = new Temperature(23, true); } [JsonConverter(typeof(TemperatureConverter))] public struct Temperature { public Temperature(int degrees, bool celsius) { Degrees = degrees; IsCelsius = celsius; } public int Degrees { get; } public bool IsCelsius { get; } public bool IsFahrenheit => !IsCelsius; public override string ToString() => $"{Degrees}{(IsCelsius ? "C" : "F")}"; public static Temperature Parse(string input) { int degrees = int.Parse(input.Substring(0, input.Length - 1)); bool celsius = input.Substring(input.Length - 1) == "C"; return new Temperature(degrees, celsius); } } public class TemperatureConverter : JsonConverter<Temperature> { public override Temperature Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Temperature.Parse(reader.GetString()!); public override void Write(Utf8JsonWriter writer, Temperature temperature, JsonSerializerOptions options) => writer.WriteStringValue(temperature.ToString()); }