1. 程式人生 > >使用 C# 9 的records作為強型別ID - JSON序列化

使用 C# 9 的records作為強型別ID - JSON序列化

![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210118_060642.png) 在本系列的上一篇文章中,我們注意到強型別ID的實體,序列化為 JSON 的時候報錯了,就像這樣: ```csharp { "id": { "value": 1 }, "name": "Apple", "unitPrice": 0.8 } ``` 不過想了一下,這樣的意外也是在意料之中的,強型別ID是record型別,而不是原始型別,因此將其序列化為一個物件是有意義的,但這顯然不是我們想要的……讓我們看看如何解決這個問題。 ### System.Text.Json 在最新版本的ASP.NET Core(從3.0)中,預設的JSON序列化程式是`System.Text.Json`,因此讓我首先介紹這種。 為了將強型別的id序列化為其值而不是物件,我們需要編寫一個通用的 JsonConverter: ```csharp public class StronglyTypedIdJsonConverter : JsonConverter where TStronglyTypedId : StronglyTypedId where TValue : notnull { public override TStronglyTypedId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType is JsonTokenType.Null) return null; var value = JsonSerializer.Deserialize(ref reader, options); var factory = StronglyTypedIdHelper.GetFactory(typeToConvert); return (TStronglyTypedId)factory(value); } public override void Write(Utf8JsonWriter writer, TStronglyTypedId value, JsonSerializerOptions options) { if (value is null) writer.WriteNullValue(); else JsonSerializer.Serialize(writer, value.Value, options); } } ``` 邏輯很簡單,對於直接讀取 id.value, 對於反序列化,建立一個強型別id的例項,然後給它賦值。 然後在啟動類中配置: ```csharp services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add( new StronglyTypedIdJsonConverter()); }); ``` 現在拿到了想要的結果: ```csharp { "id": 1, "name": "Apple", "unitPrice": 0.8 } ``` 真好!不過,還有有一個問題:我們只為添加了一個對於ProductId的轉換器,但我不想為每種型別的強型別ID新增另一個轉換器!我們想要一個適用於所有強型別id的轉換器……,現在可以建立一個轉換器工廠(ConverterFactory),就像下邊這樣: ```csharp public class StronglyTypedIdJsonConverterFactory : JsonConverterFactory { private static readonly ConcurrentDictionary Cache = new(); public override bool CanConvert(Type typeToConvert) { return StronglyTypedIdHelper.IsStronglyTypedId(typeToConvert); } public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { return Cache.GetOrAdd(typeToConvert, CreateConverter); } private static JsonConverter CreateConverter(Type typeToConvert) { if (!StronglyTypedIdHelper.IsStronglyTypedId(typeToConvert, out var valueType)) throw new InvalidOperationException($"Cannot create converter for '{typeToConvert}'"); var type = typeof(StronglyTypedIdJsonConverter<,>).MakeGenericType(typeToConvert, valueType); return (JsonConverter)Activator.CreateInstance(type); } } ``` 首先我們檢視需要轉換的型別,檢查它是否實際上是強型別的id,然後為該型別建立特定轉換器的例項,我們添加了一些快取,避免每次都進行反射工作。 現在,我們沒有新增特定的JsonConvert,只是添加了一個Factory,然後在啟動檔案修改,現在,我們的轉換器將應用於每個強型別ID ```csharp services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add( new StronglyTypedIdJsonConverterFactory()); }); ``` ### Newtonsoft.Json 如果您的專案使用的是Newtonsoft.Json進行JSON序列化,那就很簡單了。 當它序列化一個值時,Newtonsoft.Json 查詢一個compatible JsonConverter,如果找不到,就查詢一個TypeConverter, 如果TypeConverter存在,並且可以將值轉換為string,那麼它把值序列化為字串, 因為我們之前定義了 TypeConverter,Newtonsoft.Json查詢到了,我得到以下結果: ```csharp { "id": "1", "name": "Apple", "unitPrice": 0.8 } ``` 幾乎是正確的……除了id值不應序列化為字串,而應序列化為數字,如果id值是GUID或字串而不是int,那就很好,則需要編寫一個自定義轉換器。 它和 `System.Text.Json` 的轉換器非常相似,不同之處在於Newtonsoft.Json沒有轉換器工廠(ConvertFactory)的概念,相反,我們將編寫一個非泛型轉換器: ```csharp public class StronglyTypedIdNewtonsoftJsonConverter : JsonConverter { private static readonly ConcurrentDictionary Cache = new(); public override bool CanConvert(Type objectType) { return StronglyTypedIdHelper.IsStronglyTypedId(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var converter = GetConverter(objectType); return converter.ReadJson(reader, objectType, existingValue, serializer); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value is null) { writer.WriteNull(); } else { var converter = GetConverter(value.GetType()); converter.WriteJson(writer, value, serializer); } } private static JsonConverter GetConverter(Type objectType) { return Cache.GetOrAdd(objectType, CreateConverter); } private static JsonConverter CreateConverter(Type objectType) { if (!StronglyTypedIdHelper.IsStronglyTypedId(objectType, out var valueType)) throw new InvalidOperationException($"Cannot create converter for '{objectType}'"); var type = typeof(StronglyTypedIdNewtonsoftJsonConverter<,>).MakeGenericType(objectType, valueType); return (JsonConverter)Activator.CreateInstance(type); } } public class StronglyTypedIdNewtonsoftJsonConverter : JsonConverter where TStronglyTypedId : StronglyTypedId where TValue : notnull { public override TStronglyTypedId ReadJson(JsonReader reader, Type objectType, TStronglyTypedId existingValue, bool hasExistingValue, JsonSerializer serializer) { if (reader.TokenType is JsonToken.Null) return null; var value = serializer.Deserialize(reader); var factory = StronglyTypedIdHelper.GetFactory(objectType); return (TStronglyTypedId)factory(value); } public override void WriteJson(JsonWriter writer, TStronglyTypedId value, JsonSerializer serializer) { if (value is null) writer.WriteNull(); else writer.WriteValue(value.Value); } } ``` 然後在啟動檔案中這樣設定: ```csharp services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.Converters.Add( new StronglyTypedIdNewtonsoftJsonConverter()); }); ``` 然後,我們得到了預期的結果,輸出的結果是這樣: ```csharp { "id": 1, "name": "Apple", "unitPrice": 0.8 } ``` > 原文作者: thomas levesque > 原文連結:[https://thomaslevesque.com/2020/12/07/csharp-9-records-as-strongly-typed-ids-part-3-json-serialization/](https://thomaslevesque.com/2020/12/07/csharp-9-records-as-strongly-typed-ids-part-3-json-serialization/ "https://thomaslevesque.com/2020/12/07/csharp-9-records-as-strongly-typed-ids-part-3-json-serialization/") ### 最後 歡迎掃碼關注我們的公眾號 【全球技術精選】,專注國外優秀部落格的翻譯和開源專案分享,也可以新增QQ群 897216102

相關推薦

使用 C# 9records作為型別ID - JSON序列

![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210118_060642.png) 在本系列的上一篇文章中,我們注意到強型別ID的實體,序列化為 JSON 的時候報錯了,就像這樣: ```csharp {

使用 C# 9records作為型別ID - 初次使用

![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210116_124242.png) ### 強型別ID 實體通常是整數,GUID或者string型別,因為資料庫直接支援這些型別,但是,如果實體的ID的型別是一樣的,

使用 C# 9records作為型別ID - 路由和查詢引數

![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210117_120835.png) 上一篇文章,我介紹了使用 C# 9 的record型別作為強型別id,非常簡潔 ```csharp public record P

C# Json序列 資料協定型別 無法反序列 因為未找到必需的資料成員

反序列化的時候出現了下面的錯誤資訊。System.Runtime.Serialization.SerializationException: 資料協定型別“TestEntity”無法反序列化,因為未找到

C#.NET WebApi返回各種型別(圖片/json資料/字串),.net圖片轉二進位制流或byte

using System.IO; /// <summary> /// WebApi返回圖片 /// </summary> public HttpResponseMessage GetQrCode() {     var imgPath = @"D

C# Json序列、反序列之Dictionary 字典型別

前提 先引用 Newtonsoft.Json.dll 1.將字典轉化成字串 class Program { static void Main(string[] args) { Dictiona

C# Json序列和反序列

ava script brush arp turn bject new c# 序列化 using System.Web.Script.Serialization; public class JsonHelper { public static string Ob

c# json 序列時遇到錯誤 error Self referencing loop detected for type

red elf 錯誤 serial bject cin nbsp for serialize 參考網址:http://blog.csdn.net/adenfeng/article/details/41622255   在寫redis緩存幫助類的時候遇到的這個問題,本來打算

c#——Enum之Json序列

象中包含列舉型別,在序列化成Json字串的時候,顯示的是列舉型別對應的數字。 需要在JSON轉化的時候做一些操作,使之顯示字串 在列舉型別上新增屬性標籤   [JsonConverter(typeof(StringEnumConverter))] 舉例如下:

JSON 序列與反序列(二)使用TypeReference 構建型別安全的異構容器

原文連結:https://www.cnblogs.com/yuyutianxia/p/6051682.html 1. 泛型通常用於集合,如Set和Map等。這樣的用法也就限制了每個容器只能有固定數目的型別引數,一般來說,這也確實是我們想要的。 然而有的時候我們需要更多的靈活性,如資料庫可以用

2.9 JSON讀寫1:JSON序列

概述 序列化:將Go資料轉化為json字串 定義結構體 type Person struct { Name string Age int Sex

python使用json序列datetime型別問題處理

使用python的json模組序列化時間或者其他不支援的型別時會拋異常,例如下面的程式碼: # -*- coding: cp936 -*-from datetime import datetime import json if __name__=='__main__'

JSON序列與反序列匿名型別

一、序列化匿名型別 1.序列化一個var型別 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using Gon

C# json 序列 匿名物件序列號 指定物件序列

一、序列化 通常我們返回json物件給客戶端,需要新建一個類,因為有些資料對方是不需要, 就像一個類Person,裡面有欄位Name、Photo,而對方有要Photo也有不要Photo的,這個時候我們通過序列化 類指定 1.引入System.Web.Ext

java json序列日期型別

做介面開發時經常需要給前端返回日期資料,比如生日、建立時間、更新時間等。我們一般是建一個bean,將定義所需要的欄位,並和資料庫的欄位相對應。雖然資料庫的欄位是日期型別的,但bean的欄位定義在String就行了,看下面的測試程式碼: package co

【轉】c#--json序列與反序列

原文連結http://blog.csdn.net/gf771115/article/details/27114257 建立類 public class Person { private string name; public st

C#使用NewtonSoft進行Json序列,設定欄位首字母小寫方法

看以下類定義: public class TemplateFormValue { [JsonProperty("formId")] public int FormId { set; get; }

C# LitJson Json序列簡單使用

Model public class point { public string name{ get; set;} public string age{ get; set;}

C# 利用Newtonsoft.Json進行Json序列與反序列

我們可以通過DataContractJsonSerializer類來序列化一個物件為json字串。 public class JsonConvert<T> { public stat

c# 使用 Newtonsoft.Json 序列json字串以及,反序列物件

1. 序列化 物件 /** 使用 Newtonsoft.Json 序列化物件 **/ [WebMethod] public String getPersonInfos() { // 初始化資料