JSON.NET で ISO8601 規格の日付時刻文字列を JObject に DateTimeOffset として読み込む

表紙

◆ はじめに

今さら JSON.NET かよって感じですが、仕事中にはまったのでメモ。
やりたいことは、JSON 内の ISO8601規格の日付時刻文字列を一旦 JObject に変換して、
変換した JObject から DatetimeOffset の値を取得したい。

◆ 何も考えずにやるとこうなる

素直に実装してみる。

Program.cs

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;

namespace DateTimeOffsetParseSample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var stream = new StreamReader(@"Json\sample_data.json"))
            {
                var json = stream.ReadToEnd();

                // json -> JObject
                var data = JsonConvert.DeserializeObject<JObject>(json);

                // JObject から DateTimeOffset 取得
                var createdAtUtc = data.Value<DateTimeOffset>("created_at_utc");
                var createdAtJst = data.Value<DateTimeOffset>("created_at_jst");

                Console.WriteLine(createdAtUtc);
                Console.WriteLine(createdAtJst);
            }
            Console.ReadLine();
        }
    }
}

Json\sample_data.json

{
  "created_at_utc": "2019-02-11T04:50:40Z",
  "created_at_jst": "2019-02-11T13:50:40+09:00"
}

実行してみるとわかるが、var createdAtUtc = data.Value<DateTimeOffset>("created_at_utc");の部分で例外が発生する。

System.InvalidCastException
  HResult=0x80004002
  Message='System.DateTime' から 'System.DateTimeOffset' への無効なキャストです。
  Source=mscorlib

なんでや。Value<DateTimeOffset>してるのに。

◆ 解決方法

結論 DeserializeObject の引数で、日付変換設定を渡してあげれば良い。

Program.cs

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;

namespace DateTimeOffsetParseSample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var stream = new StreamReader(@"Json\sample_data.json"))
            {
                var json = stream.ReadToEnd();

                // json -> JObject
                var settings = new JsonSerializerSettings
                {
                    DateParseHandling = DateParseHandling.DateTimeOffset,
                    DateFormatHandling = DateFormatHandling.IsoDateFormat,
                    DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind
                };
                var data = JsonConvert.DeserializeObject<JObject>(json, settings);

                // JObject から DateTimeOffset 取得
                var createdAtUtc = data.Value<DateTimeOffset>("created_at_utc");
                var createdAtJst = data.Value<DateTimeOffset>("created_at_jst");

                Console.WriteLine(createdAtUtc);
                Console.WriteLine(createdAtJst);
            }
            Console.ReadLine();
        }
    }
}

結果

2019/02/11 4:50:40 +00:00
2019/02/11 13:50:40 +09:00

めでたし、めでたし。

◆ まとめ

こんなことやりたい人がどれだけいるのかはわからないけど、知ってればなんてことない話。
ちなみに、JObject 経由しない場合だと、何も考えずに変換できる。なんでや。
あと、JSON.NET のキャラクターはなんかむかつく。以上。