dasukoの技術ブログ

現役エンジニアのブログです。

【Unity】Unity標準のJsonUtilityを使ってシリアライズ

はじめに

UnityでJSONデータをシリアライズ、でシリアライズといった操作をする時に、どうやって実装されていますか?

Unityには標準でJSONデータを操作するための機能があります。

それがJsonUtilityです。

今回はこのJsonUtilityについてご紹介します。

公式ドキュメントはこちらです。

UnityEngine.JsonUtility - Unity スクリプトリファレンス

まずは各メソッドについて説明します。

ToJson

public static string ToJson (object obj);

オブジェクトをJsonフォーマットの文字列へと変換します。

※publicフィールド、もしくはSerializeField Attributeが指定されたオブジェクトが変換対象となります。

FromJson

public static T FromJson<T> (string json);

Jsonフォーマットの文字列からT型のオブジェクトを生成します。

FromJsonOverwrite

public static void FromJsonOverwrite (string json, object objectToOverwrite);

json引数にJsonフォーマットの文字列を渡し、それをobjectToOverwrite引数に設定したオブジェクトに対して上書きする。

実行速度を計測してみる

以下のようなスクリプトを追加して、実行速度を計測してみました。

stringのリスト

まずはstringのリストのフィールドをもったクラスで試してみます。

Serializableなし

using System.Collections.Generic;
using UnityEngine;
using System.Diagnostics;

public class JsonUtilityTest : MonoBehaviour
{
    void Start()
    {
        var data = new Data();
        for (var i = 0; i < 100000; ++i)
        {
            data.list.Add("testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest");
        }

        var stopwatch = Stopwatch.StartNew();

        stopwatch.Start();
        var json = JsonUtility.ToJson(data);
        stopwatch.Stop();
        UnityEngine.Debug.Log("---------ToJson-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
        UnityEngine.Debug.LogFormat("json length:{0}", json.Length);

        stopwatch.Restart();

        var data2 = JsonUtility.FromJson<Data>(json);
        stopwatch.Stop();

        UnityEngine.Debug.Log("---------FromJson-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);

        stopwatch.Restart();

        var data3 = new Data();
        JsonUtility.FromJsonOverwrite(json, data3);
        stopwatch.Stop();

        UnityEngine.Debug.Log("---------FromJsonOverwrite-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
    }
}

public class Data
{
    public List<string> list = new List<string>();
}

これを実行すると以下のようになりました。

f:id:dasuko:20200929215857p:plain

Serializableあり

次に、Dataクラスに以下のようにSerializable Attributeを指定してみます。

[System.Serializable]
public class Data
{
    public List<string> list = new List<string>();
}

結果はこんな感じになりました。

FromJson使うならSerializableつけた方がいいけど、FromJsonOverwite使う場合は不要そう。

f:id:dasuko:20200929215957p:plain

boolのリスト

stringのリストで検証したので、次はboolのリストで検証してみました。

using System.Collections.Generic;
using UnityEngine;
using System.Diagnostics;

public class JsonUtilityTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        var data = new Data();
        for (var i = 0; i < 10000000; ++i)
        {
            data.list.Add(true);
        }

        var stopwatch = Stopwatch.StartNew();

        stopwatch.Start();
        var json = JsonUtility.ToJson(data);
        stopwatch.Stop();
        UnityEngine.Debug.Log("---------ToJson-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
        UnityEngine.Debug.LogFormat("json length:{0}", json.Length);

        stopwatch.Restart();

        var data2 = JsonUtility.FromJson<Data>(json);
        stopwatch.Stop();

        UnityEngine.Debug.Log("---------FromJson-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);

        stopwatch.Restart();

        var data3 = new Data();
        JsonUtility.FromJsonOverwrite(json, data3);
        stopwatch.Stop();

        UnityEngine.Debug.Log("---------FromJsonOverwrite-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
    }
}

public class Data
{
    public List<bool> list = new List<bool>();
}

今回もDataクラスにSerializableをつけたものと、つけていないもので比較してみました。

Serializableなし

f:id:dasuko:20200929222529p:plain

Serializableあり

f:id:dasuko:20200929222549p:plain

やはり、結果は同じようにSerializableをつけるならFromJsonを使った方がよさそうですね

Listをそのまま渡してみる

以下のようにクラスを定義せず、リストをそのまま渡してみます。

using System.Collections.Generic;
using UnityEngine;
using System.Diagnostics;

public class JsonUtilityTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        var data = new List<string>();
        for (var i = 0; i < 100000; ++i)
        {
            data.Add("testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest");
        }

        var stopwatch = Stopwatch.StartNew();

        stopwatch.Start();
        var json = JsonUtility.ToJson(data);
        stopwatch.Stop();
        UnityEngine.Debug.Log("---------ToJson-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
        UnityEngine.Debug.LogFormat("json length:{0}", json.Length);
        UnityEngine.Debug.Log("json:" + json);
        stopwatch.Restart();

        var data2 = new List<string>();
        JsonUtility.FromJsonOverwrite(json, data2);
        stopwatch.Stop();

        UnityEngine.Debug.Log("---------FromJsonOverwrite-----------");
        UnityEngine.Debug.LogFormat("Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);

        UnityEngine.Debug.Log("length:" + deserializedData.Count);
    }
}

すると、以下のようにjsonが正しく生成されていないのがわかります。

JsonUtilityを使う場合はクラスを定義する必要があります。

f:id:dasuko:20200929223459p:plain

最後に

今回はJsonUtilityについてご紹介しました。

Jsonからオブジェクトを生成する方法は二通りありましたが、

再利用したり、上書きしたりすることがあるならFromJsonOverwrite、

それ以外はFromJsonを使うといった使い分けをするのがよさそうですね。