[.NET Core] System.Text.Json 添加 Customize JsonConverter

展示使用 .NET Core 內建的 System.Text.Json JSON Library 如何自訂 JSON 的轉換器。

 

 

 

前言


雖然 .NET Core 3.0 之後,官方就已經有提供 System.Text.Json Library 用來做 JSON 轉換了,但是因為這個 Library 預設有支援的 JSON 格式比較少,所以之前有些專案就還是選擇用回 Newtonsoft.Json 來解決。
 

對怎麼在 .NET Core 3.0 後使用 Newtonsoft.Json 有興趣的夥伴可以參考我的上一篇文章 👇

[.NET Core] .NET Core 改用 Newtonsoft.Json | K. C. - 點部落 (dotblogs.com.tw)
 

只不過隨著 Newtonsoft.Json 的作者加入微軟,有些人認為 Newtonsoft.Json 未來更新的機率應該是不高了,所以想說還是做個心理準備,嘗試看看使用 System.Text.Json 做到跟 Newtonsoft.Json 一樣的功能。
 

話雖如此,不過我想短時間內 Newtonsoft.Json 應該也不會那麼快下架,要使用 System.Text.Json 還是 Newtonsoft.Json 眾說紛紜,這裡附上知名討論貼文給大家參考,大家再根據自身情境選擇吧。

台灣 .NET 技術愛好者俱樂部 | 用了System.Text.Json一段時間後, 發現雷真多, 已經用回NewtonsoftJson,有沒有人跟我一樣腳上寫個慘字的. | Facebook

還有黑大的這篇文章也有寫他評估轉換的一些考量,也貼上來給大家參考

System.Text.Json!-黑暗執行緒 (darkthread.net)

 

不好意思廢話太多了 😅,以下就繼續沿用上一篇文章的情境,用特殊格式的 DateTime Property 示範~

 

 

示範專案

TargetFramework:.NET 6

專案類型:Web API

 

 

自定義 DateTime JsonConverter


新增一個 DateTimeCustomConverter.cs

using System.Text.Json;
using System.Text.Json.Serialization;

public class DateTimeCustomConverter : JsonConverter<DateTime>
{
    // Deserialization
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTime.Parse(reader.GetString() ?? string.Empty);
    }

    // Serialization
    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString());
    }
}

Read():用於 Request 進來的時候將特殊格式的 DateTime Property 轉換成 C# 可存取的 DateTime format

Write():用於 Response 出去的時候將 Property 轉換成 DateTime format 出去,如果使用 Default 的 DateTime.ToString(),則會轉換成該 Server 的 CultureInfo DateTime format

 

在 Program.cs 註冊自定義的 Converter

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = null;
        options.JsonSerializerOptions.Converters.Add(new DateTimeCustomConverter()); // + 這行
    });

這種 Global 的註冊方式,就能支援各種特殊的 DateTime format 都能成功轉換成 DateTime Type 進來 Action 了

 

如果不想註冊成 Global 通用,也能 only By 單個 Property 設置

[JsonConverter(typeof(DateTimeCustomConverter))]
public DateTime StartDateTime { get; set; }

☝ 這樣設置的話就只有 StartDateTime 可以接受特殊的 JSON DateTime format,其他 Property 照舊

 


 

自定義 Response JSON Format


上一章的設置只 for Request Property,而如果希望 Response 出去的 DateTime format 要能夠是特定的格式,就還需要對 Write() 動手腳

 

DateTimeCustomConverter.cs

using System.Text.Json;
using System.Text.Json.Serialization;

public class DateTimeCustomConverter : JsonConverter<DateTime>
{
    private readonly string _serializationFormat;
    public DateTimeCustomConverter() : this(null) { }

    public DateTimeCustomConverter(string? serializationFormat)
    {
        this._serializationFormat = serializationFormat ?? "yyyy-MM-dd HH:mm:ss.fff";
    }

    // Deserialization
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTime.Parse(reader.GetString() ?? string.Empty);
    }

    // Serialization
    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(_serializationFormat));
    }
}

添加 Constructor(建構函式),接受可以從建構子帶入要自訂的 format,沒有帶的話就預設 yyyy-MM-dd HH:mm:ss.fff

DateTime 的 .ToString() 就指定 format 為 serializationFormat

 

如果 Response 也是 Global 用,在 Program.cs 註冊時指定即可,整個專案的每個 DateTime 都會統一使用這種 format Response

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new DateTimeCustomConverter("yyyy/MM/ddTHH\\:mm\\:ss.fffffffzzz"));
    });

 

但是如果只是想 By 單個 Property 設置,就會發現無法用前一章 Request 那種的 Attribute 寫法設置,因為這種寫法無法帶入 Constructor 的參數

 

所以這裡我再新增一個 JsonCustomConverterAttribute.cs

using System.Text.Json.Serialization;

[AttributeUsage(AttributeTargets.Property)]
public class JsonCustomConverterAttribute : JsonConverterAttribute
{
    private readonly string? _serializationFormat;

    public JsonCustomConverterAttribute() : this(null) { }

    public JsonCustomConverterAttribute(string? serializationFormat)
    {
        this._serializationFormat = serializationFormat ?? "yyyy-MM-dd HH:mm:ss.fff";
    }

    public override JsonConverter? CreateConverter(Type typeToConvert)
    {
        if (typeToConvert != typeof(DateTime) && typeToConvert != typeof(DateTime?))
            throw new Exception("Can only use this attribute on DateTime properties");

        return new DateTimeCustomConverter(_serializationFormat);
    }
}

多包一層 Attribute 來達到能傳入各種不同 format 設置的功能

 

Property 改設置此 Attribute

[JsonCustomConverter("yyyy/MM/ddTHH\\:mm\\:ss.fffffffzzz")]
public DateTime StartDateTime { get; set; }

 

設置完後,各個 Property 就能各別 Response 出不同格式的 DateTime 了

 

End

 

 

延伸閱讀 - microsoft 官方教學

如何撰寫 JSON 序列化的自訂轉換器 - .NET | Microsoft Learn