JSON.NETはConverterを書かなくてもプロパティを読んでくれてjsonに変換してくれるのですが、変換された形式が望んでいるものとは違うこともあります。
{
"title": "HelloWorld!",
"tags": [ "Diary", "Programming", "C#" ],
"content": "...",
"comments": [
{
"author": "Alice",
"content": "hogehoge!"
},
{
"author": "Bob",
"content": "piyopiyo!"
}
]
}
このjsonを以下のようなクラスに変換する。
public class Post
{
public string Title { get; set; }
public string Content { get; set; }
public IList<Tag> Tags { get; set; } = new List<Tag>();
public IList<Comment> Comments { get; set; } = new List<Comment>();
}
public class Tag
{
public string Name { get; set; }
}
public class Comment
{
public string Author { get; set; }
public string Content { get; set; }
}
JsonConverterを継承したクラスを作ることで特定のクラスのCustom Converterを作ることができます。
継承したクラスでオーバーライドするメソッドは以下。
class JsonConverter
{
public bool CanConvert(Type objectType);
public object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer);
public void WriteJson(JsonWriter writer, object value, JsonSerializer serializer);
}
そしてこんなふうに実際のConverterは以下のようになる。
/// <summary>
/// Postをjsonに変換
/// </summary>
public class PostConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(Post);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var post = new Post();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject) break;
if (reader.TokenType == JsonToken.PropertyName)
{
var prop = reader.Value?.ToString();
reader.Read();
switch (prop)
{
case "tags":
post.Tags = new JsonSerializer().Deserialize<IList<Tag>>(reader);
break;
case "comments":
post.Comments = new JsonSerializer().Deserialize<IList<Comment>>(reader);
break;
case "title":
post.Title = reader.Value.ToString();
break;
case "content":
post.Content = reader.Value.ToString();
break;
}
}
}
return post;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var post = value as Post;
if (post == null) return;
writer.WriteStartObject();
writer.WritePropertyName("title");
writer.WriteValue(post.Title);
writer.WritePropertyName("content");
writer.WriteValue(post.Content);
writer.WritePropertyName("tags");
JToken.FromObject(post.Tags).WriteTo(writer);
writer.WritePropertyName("comments");
JToken.FromObject(post.Comments).WriteTo(writer);
writer.WriteEndObject();
}
}
/// <summary>
/// Tagをjsonに変換
/// </summary>
public class TagConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(Tag);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tag = new Tag();
tag.Name = reader.Value.ToString();
return tag;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var tag = value as Tag;
if (tag == null) return;
writer.WriteValue(tag.Name);
}
}
/// <summary>
/// Commentをjsonに変換
/// </summary>
public class CommentConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(Comment);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var props = new string[] { "author", "content" };
var comment = new Comment();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject) break;
if (reader.TokenType == JsonToken.PropertyName)
{
var prop = reader.Value.ToString();
reader.Read();
switch (prop)
{
case "author":
comment.Author = reader.Value.ToString();
break;
case "content":
comment.Content = reader.Value.ToString();
break;
}
}
}
return comment;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var comment = value as Comment;
if (comment == null) return;
writer.WriteStartObject();
writer.WritePropertyName("author");
writer.WriteValue(comment.Author);
writer.WritePropertyName("content");
writer.WriteValue(comment.Content);
writer.WriteEndObject();
}
}
ちょっと長いけど、それぞれでやってることは
CanConvert
で変換できるクラスを指定する。
ReadJson
ではreader
から一つずつトークンを読み込み、インスタンスに値を次々設定していく。
WriteJson
は引数で変換するインスタンスが渡されるので、writer
に書き込む。
各クラスにJsonConverter
属性を指定します。
[JsonConverter(typeof(PostConverter))]
public class Post
{
...
}
[JsonConverter(typeof(TagConverter))]
public class Tag
{
...
}
[JsonConverter(typeof(CommentConverter))]
public class Comment
{
...
}
これだけ書いたらDeserializeObject
で変換できる。
var post = JsonConvert.DeserializeObject<Post>(jsonText);