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

2018/10/04

[C#] Cryptowatch API で取得した JSON をデシリアライズする

event_note2018/10/04 8:25

予想外にはまったのでメモしておきます。
パーサーは Newtonsoft.Json(JSON.NET) を使用します。

Cryptowatch API で取得したデータは以下のような JSON 形式となっています。

{
  "result": {
    "60": [
      [1481634360, 782.14, 782.14, 781.13, 781.13, 1.92525],
      [1481634420, 782.02, 782.06, 781.94, 781.98, 2.37578],
      [1481634480, 781.39, 781.94, 781.15, 781.94, 1.68882],
      ...
    ],
    "180": [...],
    "300": [...],
    ...
    "604800": [...]
  }
}

インタプリタ言語とかだと何も考えずにデシリアライズ可能なのですが、これを C# でデシリアライズしようとすると結構面倒です。
(もっと簡単な方法があれば教えていただきたいです)

まず、result 以下のキー(60とか180とか)がクエリに応じて動的に変わるので、Dictionary を使用します。
また、値の部分が配列の入れ子になっているので、DictionaryValueList の入れ子にします。
さらに最下層の Value が整数だったり浮動小数だったり、時刻は Unix タイムなので DateTime 型に変換したいとかあるので、List の型は String にしておき、後で個別にパースしていきます。

まずデシリアライズ用の型として以下のような構造体を定義します。

public struct CryptowatchResponse
{
    public Dictionary<int, List<List<string>>> Result { get; set; }
}

以下のようにして Cryptowatch からデータを取得してデシリアライズします。

var before = DateTimeOffset.Now.ToUnixTimeSeconds();
CryptowatchResponse json;
using (var client = new HttpClient())
using (var response = client.GetAsync($"https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?before={before}").Result)
{
    var contents = response.Content.ReadAsStringAsync().Result;
    json = JsonConvert.DeserializeObject<CryptowatchResponse>(contents);
}

尚、ここでは HttpClientUsing していますが、普通はコンストラクタ等でインスタンス化して使いまわすようです。
(詳細はググってください)

さらに、ここから string 型で持っている値を適切な型に変換します。
(文字列のままで良ければこれは不要です)

まず各要素の値を入れるための構造体を以下のように定義します。

public struct CryptowatchData
{
    public DateTime Time { get; set; }
    public double Open { get; set; }
    public double High { get; set; }
    public double Low { get; set; }
    public double Close { get; set; }
    public double Volume { get; set; }
}

あとは愚直に変換してくだけです。
例えば、一分足のデータを変換したい場合は以下のような感じです。

var records = new List<CryptowatchData>();
foreach (var record in json.Result[60])
{
    records.Add(new CryptowatchData()
    {
        // unixtime から JST に変換する
        Time = DateTimeOffset.FromUnixTimeSeconds(long.Parse(record[0])).LocalDateTime,
        Open = double.Parse(record[1]),
        High = double.Parse(record[2]),
        Low = double.Parse(record[3]),
        Close = double.Parse(record[4]),
        Volume = double.Parse(record[5]),
    });
}

もっと簡単に書けないかなぁとは思いますけど、とりあえずこんな感じです。