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.
DataContractJsonSerializer
は DateTime
を UTC に変換してシリアライズしようとしますが、MinValueだった場合、JST だと -9 時間することになるので、アンダーフローを起こすのが原因らしいです。
対処方法
とりあえず思いついたのは以下です。
その1 DateTimeOffset を使う
型を変更できるなら DateTimeOffset
に変更すればシリアライズ可能になります。
その2 他のパーサーを使う
例えば、Newtonsoft.Json を使うと DateTime
が MinValue
であっても以下のように ISO 形式で文字列に変換されるため、シリアライズ可能です。
var data = new Person();
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);
出力結果
{"Birth":"0001-01-01T00:00:00","Name":"Name","Age":20}