Closed10
Ruby on RailsのJSON Serializer比較
やりたいこと
Ruby on Railsを使用したREST API実装をする際に、JSONのシリアライズを行いたい。
※ModelのデータをJSONに整形して置き換えるとか
そのままControllerでレスポンスを整形することは可能だが、Controllerの役割が増える&実装の見通しが悪くなるのでレイヤーを分けて実装するようにしたい。
そこで、いくつか存在する手法をスクラップにまとめる。
renderで自前実装
整形が特に不要であればこれで良さそう
# 1項目程度
render json: { user: @user }
# 固定文字
render json: { error: 'エラーだよ' }, status: :internal_server_error
# 配列
render json: { users: @users }
メリット
- 直感的に実装ができる
デメリット
- Controllerが肥大化する
blueprinter
公式からサンプルを拝借
class UserBlueprint < Blueprinter::Base
identifier :uuid
fields :first_name, :last_name, :email
end
# 呼び出し
puts UserBlueprint.render(user)
レスポンス
{
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
"email": "john.doe@some.fake.email.domain",
"first_name": "John",
"last_name": "Doe"
}
メリット
- JBuilderやActiveModelSerializersみたいに実装が可能
- シンプル且つ、パフォーマンスも出るような設計になっている
デメリット
- 他のライブラリと比較して文献少ないかも
- ページネーションやキャッシュといった機能が盛り込まれていない
Alba
公式からサンプルを拝借
class UserResource
include Alba::Resource
root_key :user
attributes :id, :name
attribute :name_with_email do |resource|
"#{resource.name}: #{resource.email}"
end
end
user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
UserResource.new(user).serialize
# => '{"user":{"id":1,"name":"Masafumi OKURA","name_with_email":"Masafumi OKURA: masafumi@example.com"}}'
メリット
- 速いらしい
- 実装は比較的容易らしい
- 記事や採用事例は結構ありそう
デメリット
- 比較的スター数が多くないかも(2024/09/12時点で926)
jsonapi-serializer
元々はNetflixが開発したfast_jsonapiをフォークしたものらしい。(こちらは現在メンテされていない)
公式からサンプルを拝借
class MovieSerializer
include JSONAPI::Serializer
set_type :movie # optional
set_id :owner_id # optional
attributes :name, :year
has_many :actors
belongs_to :owner, record_type: :user
belongs_to :movie_type
end
# return a hash
hash = MovieSerializer.new(movie).serializable_hash
# return serialized JSON
json_string = MovieSerializer.new(movie).serializable_hash.to_json
メリット
- 結構記事が豊富
- 速いらしい
デメリット
- 開発がちょっと止まっているかも?
- 調査時点で2023/7/31が最終コミット
Active Model Serializers
標準で入っているやつ
ActiveModel::Serialization
と ActiveModel::Serializers::JSON
の2種類あるらしい
実装例(JSONの方のみ)
class Person
include ActiveModel::Serializers::JSON
attr_accessor :name
def attributes
{'name' => nil}
end
end
person = Person.new
person.serializable_hash # => {"name"=>nil}
person.as_json # => {"name"=>nil}
person.to_json # => "{\"name\":null}"
person.name = "Bob"
person.serializable_hash # => {"name"=>"Bob"}
person.as_json # => {"name"=>"Bob"}
person.to_json # => "{\"name\":\"Bob\"}"
メリット
- Rails標準である
- 実装は比較的容易
デメリット
- パフォーマンス面でやや劣る
jbuilder
公式からサンプルを拝借
ERB等のテンプレートエンジンと同じイメージだと分かりやすいかも
# app/views/messages/show.json.jbuilder
json.content format_content(@message.content)
json.(@message, :created_at, :updated_at)
json.author do
json.name @message.creator.name.familiar
json.email_address @message.creator.email_address_with_name
json.url url_for(@message.creator, format: :json)
end
if current_user.admin?
json.visitors calculate_visitors(@message)
end
json.comments @message.comments, :content, :created_at
json.attachments @message.attachments do |attachment|
json.filename attachment.filename
json.url url_for(attachment)
end
メリット
- Rails標準
- JSONのカスタマイズが柔軟に行える
- テンプレートベース(ERB構文でロジックが組める)
デメリット
- 大規模になったり、複雑化するとパフォーマンスが低下する
- ビジネスロジックがview側に混在する恐れがある(ルールで縛れってお話しかも)
jb
jbuilderっぽい感じで実装ができて、jbuilderよりも高速
公式からサンプルを拝借
# app/views/messages/show.json.jb
json = {
content: format_content(@message.content),
created_at: @message.created_at,
updated_at: @message.updated_at,
author: {
name: @message.creator.name.familiar,
email_address: @message.creator.email_address_with_name,
url: url_for(@message.creator, format: :json)
}
}
if current_user.admin?
json[:visitors] = calculate_visitors(@message)
end
json[:comments] = @message.comments.map do |comment|
{
content: comment.content,
created_at: comment.created_at
}
end
json[:attachments] = @message.attachments.map do |attachment|
{
filename: attachment.filename,
url: url_for(attachment)
}
end
json
メリット
- jbuilderと比較して高速&シンプルな実装
デメリット
- jbuilderと同様、ビジネスロジックがview側に混在する恐れがある
active_model_serializers
公式だと思ったらまさかのサードパーティでした(名前が公式と一緒のgem)
長期間メンテされていないので、採用は見送り。
代替案が提示されている。
このスクラップは3ヶ月前にクローズされました