JSON処理をわかりやすくするGemを作った

# 背景

## RubyでのJson処理

RubyでAPIを叩いて処理するというコードを書くときがあるとします。
そのAPIレスポンスがJSONで以下のように帰ってくるとします。

```json:sample.json
{
  "id": 1,
  "title": "テスト記事",
  "description": "説明文",
  "writer": {
    "id": 1,
    "name": "なまえ",
    "profile": "プロフィール ",
    "picture": {
      "id": 1,
      "url": "https://test.jp/test.jpg"
  },
  "tags": [
    {
      "id": 1,
      "name": "Ruby"
    },
    {
      "id": 2,
      "name": "Kotlin"
    }
  ],
  "picture": {
    "id": 1,
    "url": "https://test.jp/test.jpg"
  }
}
```

これをRubyで処理して、Articleとして保存というようなのを書こうとすると

```ruby
url = 'APIのURL'
uri = URI::parse(url)
res = Net::HTTP.get(uri)
result = JSON.parse(res)

Post.create(title: result['title'], img_url: result['picture']['url'])

result['tags'].each do |tag|
  HashTag.create(name: tag['name'])
end
```

という風に書くことになると思います。

こういったようにハッシュ化されたJSONを扱う時以下のようなことが問題になります

* レスポンスの形式が何処かにまとまっていない、書いた人の頭のなかにある
* JSONが複雑になるほどコードが大変になる
* テストしにくい

## AndroidでのJson処理

Androidでは以下のように行えます。
※ KotlinでのRetrofit + Moshiの場合

```kotlin
data class Article(val id: Int,
                   val title: String,
                   val description: String,
                   val writer: Writer,
                   val tags: List<Tag>,
                   val picture: Picture)

data class Writer(val id: Int,
                  val name: String,
                  val profile: String,
                  val picture: Picture)

data class Tag(val id: Int,
               val name: String)

data class Picture(val id: Int,
                   val url: String)
```

上記の用に定義しておけばJSON形式のレスポンスを、POJO(Plain Old Java Object)へ変換してくれます。
APIのレスポンスをオブジェクトにして持っておくことで、色々な処理が楽になります。

今回作ったライブラリの目的は、JSON形式のレスポンスを、PORO(Plain Old Ruby Object)へ変換することです。
こういったような処理をRubyでも実現して、Json周りの簡略化を行おうとしました。

# 作ったライブラリ

https://rubygems.org/gems/ruson

https://github.com/klriutsa/ruson


## 使い方

Ruson::Baseを継承したモデルを作ります
fieldsというメソッドを定義して、その中にfieldを定義していきます。

```ruby
class Article < Ruson::Base
  def fields
    field :id
    field :title
    field :description
    field :writer, class: Writer
    field :tags, each_class: Tag
    field :picture, class: Picture
  end
end

class Writer < Ruson::Base
  def fields
    field :id
    field :name
    field :profile
    field :picture, class: Picture
  end
end

class Tag < Ruson::Base
  def fields
    field :id
    field :name
  end
end

class Picture < Ruson::Base
  def fields
    field :id
    field :url
  end
end
```

これらを使ってさっきのコードを書き直すと

```ruby
url = 'APIのURL'
uri = URI::parse(url)
res = Net::HTTP.get(uri)
article = Article.new(res)

Post.create(title: article.title, img_url: article.picture.url)

article.tags.each do |tag|
  HashTag.create(name: tag.name)
end
```

という風にわかりやすく書くことができます

また作成したオブジェクトにメソッドを持たせて

```ruby
class Article < Ruson::Base
  def fields
    field :id
    field :title
    field :description
    field :writer, class: Writer
    field :tags, each_class: Tag
    field :picture, class: Picture
  end

  def save
    Post.create(title: title, img_url: picture.url)
  end
end
```

という風に書けばロジックをオブジェクトに移せるので可読性が上がるかもしれません。

## 具体的な使い方

### 基本

```ruby
class Article < Ruson::Base
  def fields
    field :id
    field :title
  end
end
```

fieldで指定したものは、

```ruby
article.id
article.title
```

と呼べます

### ルートパラメータがある時

```json
{
  "article": {
    "id": 1,
    "title": "テスト記事",
    "description": "説明文"
  }
}
```

```ruby
article = Article.new(res, root: 'article')
```

と指定して下さい

### 名前を変えたい時

```ruby
class Article < Ruson::Base
  def fields
    field :id
    field :text, name: 'title'
  end
end
```

とすればJsonのtitleという要素をtextに入れてくれます。

### ネストしたクラスがある時

```ruby
class Article < Ruson::Base
  def fields
    field :writer, class: Writer
  end
end
```

上記の用に指定すると、指定したクラスのオブジェクトに変換します。

### ネストしたクラスのリストがある時

```ruby
class Article < Ruson::Base
  def fields
    field :tags, each_class: Tag
  end
end
```

上記の用に指定すると、指定したクラスのオブジェクトの配列に変換します。

# つぶやき

* 本当は、StringとかInteger、Nullableなどいろいろ指定したかったんですが、RubyにBooleanクラスがなくて、そこで躓いたので、一旦は断念。
* fieldsで囲む必要が無いようにしたかったけど、方法が思いつかなかったので今の方法にしました。
* もしかすると似たようなライブラリがあるかもしれないです





明日は@higopageさんの「Unityアプリを作ったときにやったことをまとめました。」です。