0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

UnityのJsonUtilityでDictionary<TKey, TValue>をシリアライズする

Posted at

皆さんJsonUtility、使ってますか?
便利ですよね。

でもこいつ、Dictionary<TKey, TValue>をそのまま渡してもシリアライズしてくれません。
なので

SerializableDictionary.cs
    [Serializable]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
    {
        [SerializeField] private List<TKey> keys = new();
        [SerializeField] private List<TValue> values = new();
        
        public void OnBeforeSerialize()
        {
            keys.Clear();
            values.Clear();
            using var e = GetEnumerator();
            while (e.MoveNext())
            {
                keys.Add(e.Current.Key);
                values.Add(e.Current.Value);
            }
        }

        public void OnAfterDeserialize()
        {
            Clear();
            var count = keys.Count;
            for (var i = 0; i < count; ++i)
            {
                this[keys[i]] = values[i];
            }
        }
    }

とか書く人がいるわけですが……

やめましょう。
別にjsonの定義に人間にとって読みやすいことが含まれているわけではないですが、このままだと

data.json
{ "keys":["alice","bob","charie"], "values":[0,1,2] }

とかになって、どう考えても辞書のデータではなくなります。(TKey=string, TValue=int)
データの数が増えてくると対応がわかりにくくなってバグのもとになります。
手動でデータいじらないとか難読化のためとかなら別にいいんですけどね

なので提案したいDictionaryのシリアライズの方法は

SerializableDictionary.cs
    [Serializable]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
    {
        [SerializeField] private List<SerializableKeyValuePair<TKey, TValue>> data = new();
        
        public void OnBeforeSerialize()
        {
            data.Clear();
            using var e = GetEnumerator();
            while (e.MoveNext())
            {
                data.Add(new SerializableKeyValuePair<TKey, TValue>(e.Current.Key, e.Current.Value));
            }
        }

        public void OnAfterDeserialize()
        {
            Clear();
            foreach (var pair in data)
            {
                this[pair.Key] = pair.Value;
            }
        }
    }

    [Serializable]
    public class SerializableKeyValuePair<TKey, TValue>
    {
        [SerializeField] public TKey Key;
        [SerializeField] public TValue Value;

        public SerializableKeyValuePair(TKey key, TValue value)
        {
            Key = key;
            Value = value;
        }
    }

という2段階のクラスを経るやり方です。

こうすると

data.json
{ "data": [ { "Key": "alice", "Value": 0 }, { "Key": "bob", "Value": 1 }, { "Key": "charie", "Value": 2 } ] }

という感じになります。(TKey=string, TValue=int)
辞書っぽいデータになりましたね。

もちろんデータ容量や難読化を考えるのであれば前者のほうがよいわけですが、なにかのデータを挿入したい場合や検索機能を使用しての値の書き換えのことを思うと一考の余地はあると思います。

前者のList<TKey>, List<TValue>に分けて保存する記事がかなり多かったのでメモしておきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?