辞書のキーに自作クラスを使ってシリアライズする
のは難しいらしい。
辞書の代わりにList<KeyValuePair<TK, TV>>
を使うと良い感じにシリアライズできます。
List<KeyValuePair<MyClass, string>>
SingleOrArrayConverter
1つ以上の値が入る(けど多くの場合は1つしか入らない)フィールドを持つJSONデータを考えます。
{ [ { "name": "apple", "color": [ "red", "green" ] }, { "name": "banana", "color": [ "yellow" ] }, { "name": "grape", "color": [ "purple" ] } ] }
ほとんどのcolor
フィールドにはbanana
やgrape
のようにひとつしか値が入らないとすると、このデータを下のように書くことができたら嬉しいです。
{ [ { "name": "apple", "color": [ "red", "green" ] }, { "name": "banana", "color": "yellow" }, { "name": "grape", "color": "purple" } ] }
JSONConverter ではSerialize/Deserializeの方法をカスタマイズすることができます。
まず、下記のようなJsonConverterを作成します。
using System; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Project.Scripts.Utility { public class SingleOrArrayConverter<T> : JsonConverter { public override bool CanWrite => false; public override bool CanConvert(Type objectType) { return objectType == typeof(List<T>); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var token = JToken.Load(reader); return token.Type == JTokenType.Array ? token.ToObject<List<T>>() : new List<T> { token.ToObject<T>() }; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var list = (List<T>)value; if (list.Count == 1) value = list[0]; serializer.Serialize(writer, value); } } }
これを使ってJsonを読み取ってあげます。適用したいfieldに対して [JsonConverter(typeof(SingleOrArrayConverter))]
をつけます。
public record Fruit { public readonly string Name; public readonly IEnumerable<string> Colors; [JsonConstructor] public Fruit( [JsonProperty("name")] string name, [JsonProperty("color")][JsonConverter(typeof(SingleOrArrayConverter<string>))] IEnumerable<string> colors) { Name = name; Colors = colors; } }
Deserializeするときは普通です。
var asset = Resources.Load<TextAsset>(path); var data = JsonConvert.DeserializeObject<IEnumerable<Fruit>>(asset.text).ToList();