辞書のキーに自作クラスを使ってシリアライズする
のは難しいらしい。
辞書の代わりに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();