背景
RubyでのJson処理
RubyでAPIを叩いて処理するというコードを書くときがあるとします。
そのAPIレスポンスが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として保存というようなのを書こうとすると
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の場合
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周りの簡略化を行おうとしました。
作ったライブラリ
使い方
Ruson::Baseを継承したモデルを作ります
fieldsというメソッドを定義して、その中にfieldを定義していきます。
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
これらを使ってさっきのコードを書き直すと
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
という風にわかりやすく書くことができます
また作成したオブジェクトにメソッドを持たせて
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
という風に書けばロジックをオブジェクトに移せるので可読性が上がるかもしれません。
具体的な使い方
基本
class Article < Ruson::Base
def fields
field :id
field :title
end
end
fieldで指定したものは、
article.id
article.title
と呼べます
ルートパラメータがある時
{
"article": {
"id": 1,
"title": "テスト記事",
"description": "説明文"
}
}
article = Article.new(res, root: 'article')
と指定して下さい
名前を変えたい時
class Article < Ruson::Base
def fields
field :id
field :text, name: 'title'
end
end
とすればJsonのtitleという要素をtextに入れてくれます。
ネストしたクラスがある時
class Article < Ruson::Base
def fields
field :writer, class: Writer
end
end
上記の用に指定すると、指定したクラスのオブジェクトに変換します。
ネストしたクラスのリストがある時
class Article < Ruson::Base
def fields
field :tags, each_class: Tag
end
end
上記の用に指定すると、指定したクラスのオブジェクトの配列に変換します。
つぶやき
- 本当は、StringとかInteger、Nullableなどいろいろ指定したかったんですが、RubyにBooleanクラスがなくて、そこで躓いたので、一旦は断念。
- fieldsで囲む必要が無いようにしたかったけど、方法が思いつかなかったので今の方法にしました。
- もしかすると似たようなライブラリがあるかもしれないです
明日は@higopageさんの「Unityアプリを作ったときにやったことをまとめました。」です。