C#序列化枚举为字符串和自定义转换器
C#序列化枚舉為字符串和自定義轉換器
我所做的項目是需要調用業務算法的,算法中有一個入參是油品的性質,這個性質有名稱、編碼、類型等屬性,其中類型是固定質量性質、體積性質和其他性質這三種,所以我把其作為枚舉類型。問題也由此產生,默認情況下,枚舉是以其整數形式進行 JSON 序列化,這就需要同研發算法的同事約定好數值的含義。但是經過協商,算法同事要求我們傳遞成字符串。因此,我們希望它們在一些情況下以字符串的形式進行序列化。本文將講解實現這一目標的各種方法。
1、枚舉序列化的默認行為
首先,我們有一個性質類:
public class Property{public string Code { get; set; }public string Name { get; set; }public PropertyType ProType { get; set; }}public enum PropertyType{Volume,Weight,Other}我們用 System.Text.Json 中的 Serialize 方法序列化一個 Property 對象:
using System.Text.Json;var json = JsonSerializer.Serialize(new Property {Code = "DEN",Name = "密度",ProType = PropertyType.Volume });Console.WriteLine(json);可以看到這個對象序列化為 Json 后的字符串:
{ "Code": "DEN", "Name": "密度", "ProType": 0}正如我們所看到的,枚舉屬性的值(PropertyType.Volume)被序列化為 JSON 字符串后表現為一個整數(0)。
作為序列化結果 JSON 的消費者,在沒有文檔的情況下,無法知道這個整數代表的業務含義。
所以,問題來了。如何將枚舉屬性序列為更直觀的字符串形式呢?即,如何將上面示例中的對象序列化為:
{ "Code": "DEN", "Name": "密度", "ProType": "Volume" }要把枚舉序列化為字符串,.Net 標準庫 System.Text.Json 和 Newtonsoft 庫都為此提供了一個轉換器,分別為 JsonStringEnumConverter 和 StringEnumConverter。下面來看看它們的用法。
2、基于標注的方式
.Net 標準庫和 Newtonsoft 庫都提供了一個轉換器特性 JsonConverterAttribute,我們可以用它來選擇性地標注要序列化為字符串形式的枚舉屬性或類型。
標注枚舉屬性
我們可以在枚舉屬性上使用 JsonConverterAttribute 特性來標注 ProType 屬性,并使用 JsonStringEnumConverter 轉換器,如下:
using System.Text.Json.Serialization;public class Property{public string Code { get; set; }public string Name { get; set; }[JsonConverter(typeof(JsonStringEnumConverter))]public PropertyType ProType { get; set; }}此時序列化的結果為:
{ "Code": "DEN", "Name": "密度", "ProType": "Volume" }Newtonsoft 庫也是類似的,把 JsonStringEnumConverter 替換為 StringEnumConverter 即可。
標注枚舉類型
JsonConverterAttribute 特性也可以應用在自定義類型上面,把它應用在 PropertyType 枚舉類型上:
using System.Text.Json.Serialization;[JsonConverter(typeof(JsonStringEnumConverter))] public enum PropertyType {Volume,Weight,Other }這樣,所有的 PropertyType 枚舉都會序列化為字符串形式。
3、基于選項的方式
在一些不方便修改類或枚舉類型定義的情況下(如調用第三方 SDK),就不能使用標注的方式將枚舉序列化為字符串了。.Net 標準庫和 Newtonsoft 庫還提供了帶有轉換選項參數的序列化方法,允許我們使用行內(Inline)方式實現這一目標。
.Net 標準庫示例如下:
public static string SerializeWithStringEnum(object obj) {var options = new JsonSerializerOptions();options.Converters.Add(new JsonStringEnumConverter());return JsonSerializer.Serialize(obj, options); }而 Newtonsoft 庫稍有不同:
public static string SerializeWithStringEnum(object obj) {var converter = new StringEnumConverter();return JsonConvert.SerializeObject(obj, converter); }4、全局注冊的方式
基于特性標注的方式讓我們可以靈活地以可控的方式來操作枚舉的序列化。而基于選項的方式允許我們在特定環境下按需處理對象的序列化。但是,有沒有辦法讓所有的枚舉在默認情況下被序列化為字符串呢?答案是有的。
對控制臺應用程序或 .Net 類庫就簡單了,直接封裝一個通用的序列化方法即可。這里我們主要關心如何在 ASP.NET Core 應用程序中注冊全局的序列化規則。
在 ASP.NET Core Web API 應用中,需要在 DI 管道中注冊枚舉轉換器,如下:
var builder = WebApplication.CreateBuilder(args);builder.Services.AddControllers().AddJsonOptions(options =>{options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());});var app = builder.Build();app.MapControllers();app.Run();在 ASP.NET Core Minimal API 中,也是需要在 DI 管道中注冊枚舉轉換器,但使用的是 Configure 方式:
var builder = WebApplication.CreateBuilder(args); builder.Services.Configure<JsonOptions>(options => {options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); });var app = builder.Build();app.MapGet("/", () => new Property {Code = "DEN",Name = "密度",ProType = PropertyType.Volume });app.Run();如上所述,我們可以通過全局注冊的方式來設置應用程序在默認情況下將枚舉序列化為字符串,這樣可以保持類的干凈,也不需要在序列化時指定明確的選項。
5、標志枚舉的序列化
標志枚舉(bit-mask 枚舉)的默認序列化輸出也是一個整數,而不是使用標志(Flag)的組合來表示。例如:
using System.Text.Json;var proTypes = PropertyType.Other | PropertyType.Weight; var json = JsonSerializer.Serialize(new { ProType = proTypes });Console.WriteLine(json);[Flags] public enum PropertyType {Volume = 0, Weight = 1, Other = 2 }序列化結果為:
{ "ProType": 3 }我們希望輸出的是枚舉的組合,結果卻是一個數字,這對消費者來說很難理解它真正的意義,我們希望它序列化為文件的組合。使用前面的枚舉字符串轉換器可以解決這個問題,以基于選項的方式為例,示例代碼如下:
using System.Text.Json; using System.Text.Json.Serialization;var options = new JsonSerializerOptions(); options.Converters.Add(new JsonStringEnumConverter());var proTypes = PropertyType.Other | PropertyType.Weight; var json = JsonSerializer.Serialize(new { ProType = proTypes }, options);Console.WriteLine(json); { "ProType": "Other, Weight" }這樣就得到我們想要的文本組合序列化結果了。
6、自定義枚舉字符串
在將枚舉序列化為字符串時,我們可能希望進行一些微調。例如,轉換為 camelCase 或其它更有意義的文本形式。
序列化為 camelCase 形式
JsonStringEnumConverter 有一個接收命名策略的重載構造函數,給其傳入 JsonNamingPolicy.CamelCase 參數即可將枚舉序列為 camelCase 文本形式,例如:
var options = new JsonSerializerOptions(); options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); var json = JsonSerializer.Serialize(new Property {Code = "DEN",Name = "密度",ProType = PropertyType.Volume });Console.WriteLine(json);序列化結果為:
{ "Code": "DEN", "Name": "密度", "ProType": "volume" }序列化為自定義文本
由于語言中的變量命名規則,枚舉成員在以常規方式序列化時并不總是能傳達有意義的文本。為了解決這個問題,我們可以使用 EnumMemberAttribute 特性,它是專門為此目的引入的。我們來定義一個新的枚舉:
public enum PropertyType {[EnumMember(Value = "體積性質")]volume,[EnumMember(Value = "質量性質")]Weight,[EnumMember(Value = "其他性質")]Other, }我們重新定義了一個 PropertyType 枚舉,在其成員上標注 EnumMember 特性,以提供更有意義的文本表達。
Newtonsoft 庫是支持的,序列化后,我們可以看到它輸出了我們想要的文本。
注:雖然 Donet 標準庫原生暫不支持將枚舉序列化為自定義的文本,但依然可以通過自定義轉換器來實現。
7、自定義轉換器
當接收到的Json格式字符串與本地已有類型不統一時,需要進行自定義的反序列化過程,反之亦然,例如Json字符串中以字符串"TRUE"表示布爾類型true(不自定義,這個過程依然走的通,只是以此舉例),以字符串"FALSE"表示布爾類型false時,需要自定義如下:
/// <summary> /// 自定義布爾類型數據轉換規則 /// </summary> public class MyBoolConverter : JsonConverter{private const string TrueStr = "TRUE";private const string FalseStr = "FALSE";public override bool CanConvert(Type objectType) => true;//反序列化 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {if (reader.ValueType == typeof(string)){if ((string)reader.Value == TrueStr){return true;}else {return false;}}return false;}//序列化 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {if (value.GetType() == typeof(bool)){bool result = (bool)value;if (result){writer.WriteValue(TrueStr);}else {writer.WriteValue(FalseStr);}}} }然后,在需要操作的類型定義中的字段/屬性中加入該特性:
private class MyClass{[JsonConverter(typeof(MyBoolConverter))]public bool MyBool; }此時
string jsonStr = @"{""MyBool"": ""TRUE""}"; MyClass1 myClass = JsonConvert.DeserializeObject<MyClass1>(jsonStr); Console.WriteLine(myClass.MyBool); //True Console.WriteLine(JsonConvert.SerializeObject(myClass)); //{"MyBool":"TRUE"}8、總結
枚舉默認是以其整數形式進行 JSON 序列化的,這要求消費者事先了解這些數字的實際含義,給代碼的理解也帶來了一定的困難。所以 .Net 基礎庫和流行的 Newtonsoft 庫都提供了將枚舉序列化為字符串的多種方法。根據需求,可以選擇使用本文講的基于特性標注的方式、基于選項參數的方式和全局方式。
另外,除了可以將枚舉序列化為成員名字符串,還可以自定義文本的輸出,如輸出為 camelCase 文本形式和自定義輸出的文本內容等。
總結
以上是生活随笔為你收集整理的C#序列化枚举为字符串和自定义转换器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 经典语句(N篇)
- 下一篇: 基于C#的安全聊天工具设计