プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2020/06/17

[C#] DateTime 型を含むデータのシリアライズ

event_note2020/06/17 0:35

DateTime 型を含むデータを Microsoft 標準の DataContractJsonSerializer を使ってシリアライズすると例外が発生しました。

環境

  • Visual Studio 2017
  • .NET Core 2.2

サンプルコード

以下のようなクラスをシリアライズしたいとします。

public class Person
{
    public DateTime Birth { get; set; } = DateTime.Today;
    public string Name { get; set; } = "Name";
    public int Age { get; set; } = 20;
}

DataContractJsonSerializer を使ってシリアライズしてみます。

static public string Serialize<T>(T value)
{
    var serializer = new DataContractJsonSerializer(typeof(T));
    var serializedData = string.Empty;
    using (var memoryStream = new MemoryStream())
    {
        serializer.WriteObject(memoryStream, value);
        serializedData = Encoding.UTF8.GetString(memoryStream.ToArray());
    }
    return serializedData;
}
var data = new Person();
var serialized = Serialize(data);
Console.WriteLine(serialized);

出力結果

{"Age":20,"Birth":"\/Date(1592319600000+0900)\/","Name":"Name"}

例外が発生するパターン

ここで、上記の DateTime 型である Birth の初期値を未設定にすると、DateTime.MinValue が初期値になりますが、この場合に以下の例外が発生します。

System.Runtime.Serialization.SerializationException : DateTime values that are greater than DateTime.MaxValue or smaller than DateTime.MinValue when converted to UTC cannot be serialized to JSON.
---- System.ArgumentOutOfRangeException : Specified argument was out of the range of valid values.

DataContractJsonSerializerDateTime を UTC に変換してシリアライズしようとしますが、MinValueだった場合、JST だと -9 時間することになるので、アンダーフローを起こすのが原因らしいです。

対処方法

とりあえず思いついたのは以下です。

その1 DateTimeOffset を使う

型を変更できるなら DateTimeOffset に変更すればシリアライズ可能になります。

その2 他のパーサーを使う

例えば、Newtonsoft.Json を使うと DateTimeMinValue であっても以下のように ISO 形式で文字列に変換されるため、シリアライズ可能です。

var data = new Person();
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);

出力結果

{"Birth":"0001-01-01T00:00:00","Name":"Name","Age":20}