9
7

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 5 years have passed since last update.

JSON.NETのCustom Converterを書く

Posted at

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);
9
7
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
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?