DateTime項目を含むJSONシリアライズ


例えば

public class Master {
    public Master(string ID, string Name, DateTime Join) {
        UserID = ID;
        UserName = Name;
        Joined = Join;
        Leaved = DateTime.MinValue;
    }

    public string UserID { get; set; }

    public string UserName { get; set; }

    public DateTime Joined { get; set; }

    public DateTime Leaved { get; set; }
}

こんなクラスがあったとしましょう。
ぶりぶりに使い込んで良い感じのシステムの一部として組み込まれていると思ってね。

ところで

諸々の要件が上がってきて、CS間でJSONデータのやり取り、ってのが必要になったとするじゃないですか―
            なったんですよ、いいですね?

まぁ、MasterクラスをJSON対応すれば良いんですけど。
こんな感じに…

[DataContract]
public class Master {
    public Master() { }

    public Master(string ID, string Name, DateTime Join) {
        UserID = ID;
        UserName = Name;
        Joined = Join;
        Leaved = DateTime.MinValue;
    }

    [DataMember(Order = 0)]
    public string UserID { get; set; }

    [DataMember(Order = 1)]
    public string UserName { get; set; }

    [DataMember(Order = 2)]
    public DateTime Joined { get; set; }

    [DataMember(Order = 3)]
    public DateTime Leaved { get; set; }



    public static Master CreateInstance(string json) {
        using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Master));
            return (Master)serializer.ReadObject(stream);
        }
    }

    public string Serialize() {
        using (MemoryStream stream = new MemoryStream()) {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(this.GetType());
            serializer.WriteObject(stream, this);
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }
}

でも、落とし穴が

例えば、

    Console.WriteLine(new Master("0000", "Toraja", DateTime.Today).Serialize());

みたいな感じでJSONが得られると思うじゃないですか、普通。
でもでも、

って怒られちゃいます。

あっちこっちでそういう状況が発生しているらしいので、皆さん解説して下さっていますので、詳しくはググってみて下さい。

一応簡単に言うと、DateTimeをシリアライズする際にその値をUTCに変換しようするんですが、その値がMinValueだった場合マイナス9時間した際にアンダーフローを起こす、と云う事らしいですけどじゃぁどうすれば良いんだよって感じです。

で、識者の意見によれば、JSONの定義にはDateTime型は含まれていないので、日付をJSONに入れたい場合には文字列として扱え!
と云うのが正論らしいです。

どう対応するか

いやーでも、ぶりぶりに使い込んでるんですよ。
今更クラス定義のDateTimeをstring型に変更しちゃったら、影響範囲がどこにまで及ぶか…


って事で、考え方を反転させるのが良いのかなぁ、と。
こう云う感じ(着目部分だけ括り出しました)。

    [IgnoreDataMember]
    public DateTime Joined { get; set; }

    [IgnoreDataMember]
    public DateTime Leaved { get; set; }

    [DataMember(Order = 2, Name = "Joined")]
    private string StringJoined {
        get { return $"{Joined:yyyyMMddHHmmss}"; }
        set { Joined = DateTime.ParseExact(value, "yyyyMMddHHmmss", CultureInfo.CurrentCulture, DateTimeStyles.None); }
    }

    [DataMember(Order = 3, Name = "Leaved")]
    private string StringLeaved {
        get { return $"{Leaved:yyyyMMddHHmmss}"; }
        set { Joined = DateTime.ParseExact(value, "yyyyMMddHHmmss", CultureInfo.CurrentCulture, DateTimeStyles.None); }
    }

内側のDatetime型のプロパティはJSONには出さないようにして、JSONに出す側のプロパティをprivateのstring型として新設します。序に名前をちょこっと変えといてあげればイイ感じかも…

で、結果は以下。

{"UserID":"0000","UserName":"Toraja","Joined":"20190723000000","Leaved":"00010101000000"}

あ、ひょっとして

グリニッジより西にある諸国では、こう云うややこしい目に合わないんだろうねぇ
きっと