Unity#JsonUtilityのデシリアライズ時の挙動確認とデシリアライズ失敗時にエラーを出させる方法

# 概要
UnityのJsonUtilityをサーバーレスポンスのデシリアライザとして使う際実用に耐えるかどうか確かめた際のログです。
めっちゃ適当で恐縮ですけど、以下のデシリアライズがちゃんとできるか確かめたものになります。

# JSONとクラスのプロパティが一致
うまくいった。まぁこれうまくいかないわけないよねぇ。

```C#
[Serializable]
public class Test {
    [SerializeField] private string aaa;
    [SerializeField] private int bbb;

    public override string ToString() {
        return $"aaa:{aaa}, bbb:{bbb}";
    }
}
```

```c#
var json = "{\"aaa\":\"test1\", \"bbb\":3 }";
var test = JsonUtility.FromJson<Test>(json);
Debug.Log(test); // aaa:test1, bbb:3
```

# リストを含んだクラスのデシリアライズ
うまくいった。

```C#
[Serializable]
public class Test {
    [SerializeField] private string aaa;
    [SerializeField] private List<string> bbb;

    public override string ToString() {
        return $"aaa:{aaa}, bbb:{string.Join(",", bbb)}";
    }
}
```

```c#
var json = "{\"aaa\":\"test1\", \"bbb\":[\"a\",\"b\",\"c\"]}";
var test = JsonUtility.FromJson<Test>(json);
Debug.Log(test); // aaa:test1, bbb:a,b,c
```

# オブジェクトがネストしたクラスのデシリアライズ
これもうまくいく。

```c#
[Serializable]
public class Test {
    [SerializeField] private string aaa;
    [SerializeField] private Test2 bbb;

    public override string ToString() {
        return $"aaa:{aaa}, bbb:[{bbb}]";
    }
}
[Serializable]
public class Test2 {
    [SerializeField] private string ccc;
    [SerializeField] private List<string> ddd;

    public override string ToString() {
        return $"ccc:{ccc}, ddd:{string.Join(",", ddd)}";
    }
}
```
```c#
var json = "{\"aaa\":\"test1\", \"bbb\":{\"ccc\": \"test2\", \"ddd\": [\"a\",\"b\",\"c\"]}}";
var test = JsonUtility.FromJson<Test>(json);
Debug.Log(test); // aaa:test1, bbb:[ccc:test2, ddd:a,b,c]
```

# Dictionary型を含んだクラスのデシリアライズ
**うまくいかない。** まぁ、これは他の記事でも読んでたから想定内。
ネストしたクラスがデシリアライズできるなら問題はないかな。
`(ただエラーが出ないのはちょっと問題だな…)`

```c#
[Serializable]
public class Test {
    [SerializeField] private string aaa;
    [SerializeField] private Dictionary<string, string> bbb;

    public override string ToString() {
        if (bbb == default) {
            return $"aaa:{aaa}, bbb:[]";
        }
        return $"aaa:{aaa}, bbb:[{string.Join(",", bbb.Select(a => a.Key + ":" + a.Value))}]";
    }
}
```

```C#
var json = "{\"aaa\":\"test1\", \"bbb\":{\"ccc\": \"test2\", \"ddd\": \"test3\"}}";
var test = JsonUtility.FromJson<Test>(json);
Debug.Log(test); // aaa:test1, bbb:[]
```

# JSONとクラスの型が違う場合
**うまくいかない。** ただエラーにならず一致しない箇所が空やnullになる。
これもエラー吐いてくれないのねん…

```c#
[Serializable]
public class Test {
    [SerializeField] private string aaa;
    [SerializeField] private string bbb;

    public override string ToString() {
        return $"aaa:{aaa}, bbb:{bbb}";
    }
}
```
```C#
var json = "{\"aaa\":\"test1\", \"bbb\":{\"ccc\": \"test2\", \"ddd\": \"test3\"}}";
var test = JsonUtility.FromJson<Test>(json);
Debug.Log(test); // aaa:test1, bbb:
```

# JSONとクラスのプロパティが一致しない場合
これもエラーは吐かれない。うーむ…。

```c#
[Serializable]
public class Test {
    [SerializeField] private string aaa;
    [SerializeField] private string bbb;

    public override string ToString() {
        return $"aaa:{aaa}, bbb:{bbb}";
    }
}
```
```C#
var json = "{\"aaa\":\"test1\", \"ccc\":\"test2\"}";
var test = JsonUtility.FromJson<Test>(json);
Debug.Log(test); // aaa:test1, bbb:
```

# ジェネリクスを使ったクラスのデシリアライズ
これはうまくいった。よかった。
これ上手くいかなかったら使用見送るとこだった…

```c#
[Serializable]
public class Test<T> {
    [SerializeField] private string aaa;
    [SerializeField] private T bbb;

    public override string ToString() {
        return $"aaa:{aaa}, bbb:[{bbb}]";
    }
}

[Serializable]
public class Test2 {
    [SerializeField] private string ccc;
    [SerializeField] private string ddd;

    public override string ToString() {
        return $"ccc:{ccc}, ddd:{ddd}";
    }
}
```
```C#
var json = "{\"aaa\":\"test1\", \"bbb\":{\"ccc\":\"test2\", \"ddd\":\"test3\"}}";
var test = JsonUtility.FromJson<Test<Test2>>(json);
Debug.Log(test); // aaa:test1, bbb:[ccc:test2, ddd:test3]
```

# 結論
ギリ使えそう。
ただ、パラメータ不一致の場合に空文字だったりnullになったりするから何かしら必須の箇所はNullチェックする方法は考えないといけなさそう。

## Nullチェックしてエラーを吐くようにする
こんな感じの作れば、デシリアライズする際にnullを許容しない変数がnullや空文字の場合、エラーにすることはできる。
もうちょい調整すればサーバーレスポンスのデシリアライザとして使えそうな気がしなくもない。

```C#
// カスタムアトリビュート作成
[System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
public class NotNull : Attribute {
}

// これを使ってデシリアライズする
public class JSONDeserializer {
    public static T FromJSON<T>(string json) {
        var res = JsonUtility.FromJson<T>(json);
        var _type = res.GetType();
        // とりあえずprivateな変数だけ。publicも見たりする場合多分ここの処理は変更が必要になる
        var fields = _type.GetFields( BindingFlags.NonPublic | BindingFlags.Instance);
        foreach (var field in fields) {
            var attr = field.GetCustomAttribute(typeof(NotNull));
            if (attr == null) {
                continue;
            }

            var val = field.GetValue(res);

            if (val == null || string.IsNullOrEmpty(val.ToString())) {
                throw new Exception("えらーだよ");
            }
        }
        return res;
    }   
}

[Serializable]
public class Test {
    
    [SerializeField][NotNull] private string aaa;
    [SerializeField][NotNull] private string bbb;

    public override string ToString() {
        return $"aaa:{aaa}, bbb:[{bbb}]";
    }
}

var json = "{\"aaa\":\"test1\", \"bbb\":{\"ccc\":\"test2\", \"ddd\":\"test3\"}}";
var test = JSONDeserializer.FromJSON<Test>(json); //ここでException「えらーだよ」がthrowされる。
Debug.Log(test); // ここまで到達しない
```