Unity で REST API を呼ぶ (そして Json を parse して Class に変換する)

# 注意
UniRx 周りの理解が甘いので、変なこと書いてるかもです。
https://www.hanachiru-blog.com/entry/2020/02/29/120000
現在この辺りを読みながら勉強中なので、何かわかれば追記、修正します。

# やること

`https://xxxxx.com/xxxxx` にアクセスすると下記のような Json を返す API を、

```json
{
    "items": [
        {
            "user_name": "post",
            "points": {
                "of_distance": 5,
                "of_coin": 5
            },
            "total_point": 10,
            "timestamp": 1592761117
        },
        {
            "user_name": "post",
            "points": {
                "of_distance": 5,
                "of_coin": 5
            },
            "total_point": 10,
            "timestamp": 1592761117
        }
    ]
}

```

Unity から呼び出して、 同じ構造を持った Class へ　Deserialize, Parse する。

# ライブラリのインポート

## UniRx

Unity  で非同期処理のタイミング調整(?)が行えるライブラリ。
async/await が使えるようになる。

書き方によっては使わなくても良さそう。

### 導入方法

AssetStore から一式インポート

<img width="1264" alt="unirx.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252342/41276f0c-19cc-1e5e-9e24-bf35dea95799.png">

https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276

## Newtonsoft.Json

Json の Deserialize, Parse をいい感じにやってくれる。

### 導入方法

https://qiita.com/kingyo222/items/11100e8f7be396b98453

この方法で NuGet を import して、 NuGet の機能で Newtonsoft.Json を取得。

<img width="320" alt="nuget.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252342/88697866-fbd3-a971-8d14-365ae64a19e7.png">

すると、 Assets/Packages/Newtonsoft.Json.12.0.3 が取得できる。


# 実装

## 通信を行うクラス

UnityWebRequest を使って json を取得して、JsonConvert.DeserializeObject を使って Deserialize, Parse を行う。

```cs
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections.Generic;
using Newtonsoft.Json;

public class HttpSample
{
    public string url;
    public Scores scores { get; private set; }

    public IEnumerator Get(System.IObserver<Scores> observer)
    {
        UnityWebRequest req = UnityWebRequest.Get(url);
        yield return req.SendWebRequest();
        if (req.isNetworkError)
        {
            Debug.Log(req.error);
        }
        else if (req.isHttpError)
        {
            Debug.Log(req.error);
        }
        else
        {
            // json が返る
            string result = req.downloadHandler.text;
            // Scores 型に変換
            this.scores = JsonConvert.DeserializeObject<Scores>();
            observer.OnNext(scores);
            observer.OnCompleted();
        }
    }

    // クラスに `[JsonObject("xxxxx")]` ,
    // クラスプロパティに `[JsonProperty("xxxxx")]` をつけると、
    // json parse 時のフォーマットを変更できる
    [JsonObject("scores")]
    public class Scores {
        [JsonProperty("items")]
        public List<Score> items;
        [JsonObject("score")]
        public class Score
        {
            [JsonProperty("user_name")]
            public string user_name;
            [JsonProperty("points")]
            public Points points;
            [JsonProperty("total_point")]
            public int total_point;
            [JsonProperty("timestamp")]
            public int timestamp;

            [JsonObject("points")]
            public class Points
            {
                [JsonProperty("of_distance")]
                public int of_distance;
                [JsonProperty("of_coin")]
                public int of_coin;
            }
        }
    }
}
```

## 呼び出すクラス

async/await を使って同期的に結果を取得し、取得したクラスを使っていく。

```cs
using UnityEngine;
using UniRx;

public class ScoresController : MonoBehaviour
{
    // UniRx を入れると async が書ける
    async void Start()
    {
        HttpSample scoreHttp = new HttpSample("https://xxxxxxxxxxxxxxx.com/api");
　　　　　// await を使うことで、結果を同期的に扱える。
        HttpSample.Scores result = await Observable.FromCoroutine<HttpSample.Scores>(observer => scoreHttp.Get(observer));
        Debug.Log(result)
    }
}
```