【Rails】便利なJbuilderの使い方|ブラウザにJSON形式のデータ表示する方法

rails-prograshi(プロぐらし)-kv Rails
記事内に広告が含まれていることがあります。

Railsでは通常のページにアクセスすれば、対象のビューファイルがHTML形式でブラウザに表示されます。

ですが、RailsをDB操作などバックエンドとして使う場合など、他のアプリケーションがアクセスしたときにDBや処理したデータをJSON形式で受け取る(APIとして利用する)ことができるととても便利な場合があります。

Railsには、Jbuilder(ジェービルダー)という、JSON形式のデータを返すことができるとても便利なgemがデフォルトで入っていいます。

あまり聞きなれない名前ですが、使い方は簡単です。

ここでは、Jbuilderとは何か?や実際の使い方について解説しています。


Jbuilderとは?

jbuilderとは、簡単に言うと、ビューファイルのJSON版で、そのファイルにアクセスするとJSON形式のデータを返します。

scaffoldを実行すると、各ビューファイルに対応するJbuilderファイルを自動で生成してくれます。

JSONとは?

JSON形式のデータとは、データをキーと値のセットにし、波カッコ{ }で囲んだデータのことです。

この、キーと値のセットをオブジェクトと呼びます。 (キーと値のセットを KV(Key-Value)と呼ぶこともあります)

基本形は次のようになります。

{ キー名: 値 } 

複数のデータを持つことができます。配列を入れることもできます。

#名前データを定義
{"name":"テスト"}

#年齢データを追加
{"name":"テスト", "age":"27"}

#新たにデータを追加
{{"name":"テスト", "age":"27"}, {"name":"テスト2", "age":"19"}}

※JSONファイルにコメントアウトはありません。便宜上#を使っています。


KVのセット毎に改行する記述もよく使われます。

#改行なしの記述
{"name":"テスト", "age":"27","hobby":["aaa", "bbb", "ccc"]}

#改行した場合
{
    "name":"テスト",
     "age":"27",
     "hobby":["aaa", "bbb", "ccc"]
}


Jbuilderのファイルとアクセス方法

Jbuilderのファイルの拡張子は、.json.jbuilderになります。

ファイルの扱い方は、ビューファイル(.html.erb)と並列として扱うと考えるとわかりやすいです。

ファイル名(〇〇.json.jbuilder)やファイル作成場所、ファイルへのアクセスの仕方はビューファイルと同様になります。

項目書き方
拡張子.json.jbuilder
設置場所ビューファイルと同じ
ファイル名ビューファイルと合わせる。〇〇.json.jbuilder
アクセスビューファイルのURIの末尾に .json をつける

例えば、rails g controller projects indexで、projectsコントローラとindexアクションを生成すると、viewsディレクトリ配下に、対応するディレクトリとビューファイルが生成されます。

views > projects > index.html.erb

またデフォルトのルーティングでは、index.html.erbを開くには、/projects/index にアクセスします。

Jbuilderを使ってJSONを返すデータにアクセスしたい場合は、ビューファイルと同じ場所に拡張子を.json.jbuilderにしたファイルを置き、中にコードを記述します。

そのファイルにアクセスするときは、URIの末尾に.jsonをつけます。これで完成です。

app
 |
 |- views
      |
      |- projects
             |- index.html.erb
             |- index.json.jbuilder

上記の場合、https://example.com/projects/index.json にアクセスすればjbuilderのファイルにアクセスすることができます。


Jbuilderの書き方

JbuilderファイルにアクセスしてJSON形式のデータを返すには決まった書き方をする必要があります。

キーと値のセットを作る

一番の基本となるキーと値を指定するには、json.set!を使ってシンボルでキー名を指定する方法と、キーと値を直接指定する方法があります。

以下の2つは同じ処理になります。

json.set! :キー名, 値

json.キー名 値
point
  • キー名は文字列の場合もダブルクオテーションは不要です。(※値は必要です)
  • json.キー名 の後ろにカンマは不要です。(あるとエラーになります)

注意点(JSON)

同じ階層で、同じキー名を指定した場合は値が上書きされます。

階層が異なったり、異なるキー名であれば、別々に出力されます。(JSONの仕様です)

実例

json.set! :名前, "テスト"
json.set! :年齢, "24"

json.性別 "女性"
json.身長 "160cm"

↓ ページにアクセスした結果(出力)

{"名前":"テスト","年齢":"24","性別":"女性","身長":"160cm"}


json.キー名とjson.set!の違いは、キー名を動的に指定できるかどうかです。

json.キー名とjson.set!の違いは、キー名を動的に指定できるかどうかです。

柔軟性が高いのはjson.set!です。json.キー名は動的にキー名を指定することができません。

json.set!はキー名を動的に指定できる

@client = { id:1, name:"test", age:'24' }

@client.each do |key, value|
    json.set! key, value
end

出力結果は、{"id":1,"name":"test","age":"24"}になります。

json.キー名はキー名を指定できない

注意点(json.キー名)

json.set!と違い動的にキー名を指定することができません。

@client = { id:1, name:"test", age:'24' }

@client.each do |key, value|
    json.key value
end


出力結果は、{"key":"24"}になります。 idやnameのデータがありません。


キーと値のセットにキー名をつける

キーと値のセットをひとまとめにして、キー名をつけるにはjson.set! :キー名 doを使います。(※キー名には「:」を忘れずに)

こちらもjson.キー名を使った書き方もできます。以下の2つは同じ処理になります。

json.set! キー名 do
  指定したキー名の中に入れるデータ
end

json.キー名 do
  指定したキー名の中に入れるデータ
end


実例

json.set! :ユーザー1 do
  json.set! :名前, "テスト"
  json.set! :年齢, "24"
end


json.ユーザー2 do
  json.名前 "テスト2"
  json.年齢 "32"
end

↓ ページにアクセスした結果(出力)

{"ユーザー1":{"名前":"テスト","年齢":"24"},"ユーザー2":{"名前":"テスト2","年齢":"32"}}


マージする

あるキーの中にデータを追加(マージ)するには、ハッシュ(変数)json.merge!を使います。

変数名 = { オブジェクト }

json.キー名 do
  json.キー名 "値"
  json.marge! 変数名
end

 


実例

books = { "本1": { "カテゴリー": "マンガ", "価格":450 } }

json.ユーザー do
  json.名前 "ユーザー1" 
  json.set! :年齢, "24"
  json.merge! books
end

↓ ページにアクセスした結果(出力)

{"ユーザー":{"名前":"ユーザー1","年齢":"24","本1":{"カテゴリー":"マンガ","価格":450}}}


nilのデータを無視する

値がnilやセットされていないデータを除外したい場合はjson.ignore_nil! を記述します。

json.ignore_nil!

nilを除外したい処理

 
注意点
  • 記述した箇所より下の処理に適用されます。
  • 対象は、nil か、データがない状態のオブジェクトです。
  • ネストしたデータにも適用されます。
  • 変数の中身には適用されません。


実例

json.ignore_nil!

books = { "本1": { "カテゴリー": "マンガ", "価格":450 }, "本2": nil }

json.ユーザー do
  json.名前 "ユーザー1" 
  json.set! :年齢, "24"
  json.set! :性別
  json.set! :身長, nil
  json.merge! books
end

↓ ページにアクセスした結果(出力)

{"ユーザー":{"名前":"ユーザー1","年齢":"24","本1":{"カテゴリー":"マンガ","価格":450},"本2":null}}

jbuilderを使って生成したオブジェクトのnilは削除されますが、変数の中に定義したnilはそのまま出力されます(出力結果はnullになります)



モデルへの適用

jbuilderの便利なところは、モデル経由で取得してきたデータ(モデルのインスタンス)にも使えるところです。

例えば、usersテーブルからすべてのデータを取得する場合、User.all が使えます。このUser.allをJSON形式で出力することができます。

モデル経由でDBを操作する方法については下記をご参考ください。

【Rails】モデルを使ってテーブルの一覧表示やデータ追加・更新・変更・削除する方法(データベース操作)


すべてのデータを抜き出す

テーブルから取得してきた全てのデータをJSON形式で返す方法は大きく3つあります。

  1. json.array!を使う
  2. キー名をつけて抜き出す
point

ここで紹介している2つの方法は、allメソッドなどモデル経由で取得したデータの型が、ActiveRecord::Relationの場合に使える方法です。(このデータをcollectionと言ったりもします)

なので、allメソッド以外にも、whereメソッドで取得したデータに対しても使えます。


▼例:User.allの実行結果

irb(main):001:0> User.all
  User Load (0.9ms)  SELECT "users".* FROM "users" /* loading for inspect */ LIMIT $1  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "test1", age: 24, created_at: "2021-07-29 04:38:52.761212000 +0000", updated_at: "2021-07-29 04:38:52.761212000 +0000">, #<User id: 3, name: "test2", age: 25, created_at: "2021-07-30 23:18:28.177139000 +0000", updated_at: "2021-07-30 23:18:28.177139000 +0000">, #<User id: 4, name: "test3", age: 28, created_at: "2021-07-30 23:19:35.242567000 +0000", updated_at: "2021-07-30 23:19:35.242567000 +0000">, #<User id: 5, name: "test4", age: 30, created_at: "2021-07-30 23:19:44.976725000 +0000", updated_at: "2021-07-30 23:19:44.976725000 +0000">, #<User id: 6, name: "test5", age: 32, created_at: "2021-07-30 23:19:54.558410000 +0000", updated_at: "2021-07-30 23:19:54.558410000 +0000">]>


json.array!を使う

最も簡単な方法は、json.array!を使う方法です。後ろにモデルクラス名.all で取得してきたデータを指定すれば、すべてのデータをJOSN形式で返すことができます。

※コンパイル後の最上位は配列になります。

変数名 = モデルクラス名.all

json.array! 変数名

ひとまとまりのデータにキー名をつデータ全体にキー名をつける場合は、json.キー名 do endの中で回します。

変数名 = モデルクラス名.all

json.キー名 do
  json.array! 変数名
end


実例

@users = User.all

json.array! @users

↓ ページにアクセスした結果(出力)

[{"id":1,"name":"test1","age":24,"created_at":"2021-07-29T04:38:52.761Z","updated_at":"2021-07-29T04:38:52.761Z"},{"id":3,"name":"test2","age":25,"created_at":"2021-07-30T23:18:28.177Z","updated_at":"2021-07-30T23:18:28.177Z"},{"id":4,"name":"test3","age":28,"created_at":"2021-07-30T23:19:35.242Z","updated_at":"2021-07-30T23:19:35.242Z"},{"id":5,"name":"test4","age":30,"created_at":"2021-07-30T23:19:44.976Z","updated_at":"2021-07-30T23:19:44.976Z"},{"id":6,"name":"test5","age":32,"created_at":"2021-07-30T23:19:54.558Z","updated_at":"2021-07-30T23:19:54.558Z"}]


キー名をつけて抜き出す

モデルクラス名.all で取得してきたデータにキー名をつけてJSON形式で返す方法です。(※キー名の中は配列になります)

1行ずつのデータをハッシュとして取り出し、json.merge!で結合します。

インスタンス変数 = モデルクラス名.all

json.キー名 インスタンス変数 do |変数名|
  json.merge! 変数名
end

出力は キー名:[ {カラム名1:値1,,,,,},{},{} ]のようになります。

実例

@users = User.all

json.users @users.each do |user|
    json.merge! user
end

↓ ページにアクセスした結果(出力)

{"users":[{"id":1,"name":"test1","age":24,"created_at":"2021-07-29T04:38:52.761Z","updated_at":"2021-07-29T04:38:52.761Z"},{"id":3,"name":"test2","age":25,"created_at":"2021-07-30T23:18:28.177Z","updated_at":"2021-07-30T23:18:28.177Z"},{"id":4,"name":"test3","age":28,"created_at":"2021-07-30T23:19:35.242Z","updated_at":"2021-07-30T23:19:35.242Z"},{"id":5,"name":"test4","age":30,"created_at":"2021-07-30T23:19:44.976Z","updated_at":"2021-07-30T23:19:44.976Z"},{"id":6,"name":"test5","age":32,"created_at":"2021-07-30T23:19:54.558Z","updated_at":"2021-07-30T23:19:54.558Z"}]}



指定したカラムのみ抜き出す

テーブルの指定したカラムのデータのみをJSON形式で返す方法は大きく3つあります。

  1. json.array!を使う(最上位が配列になります)
  2. キー名をつけて抜き出す


json.array!を使う

最も簡単な方法は、json.array!を使う方法です。後ろにモデルクラス名.all で取得してきたデータを指定し、カンマで区切って、:カラム名 を指定します。

※コンパイル後の最上位は配列になります。

変数名 = モデルクラス名.all

json.array! 変数名, :カラム名1, :カラム名2,,,

データ全体にキー名をつける場合は、json.キー名 do endの中で回します。

変数名 = モデルクラス名.all

json.キー名 do
  json.array! 変数名, :カラム名1, :カラム名2,,,
end


実例

@users = User.all

json.array! @users, :id, :name

↓ ページにアクセスした結果(出力)

[{"id":1,"name":"test1"},{"id":3,"name":"test2"},{"id":4,"name":"test3"},{"id":5,"name":"test4"},{"id":6,"name":"test5"}]


キー名をつけて抜き出す

モデルクラス名.all で取得してきたデータにキー名をつけてJSON形式で返す方法です。(※キー名の中は配列になります)

ブロックの中で欲しいカラムのみを指定します。

インスタンス変数 = モデルクラス名.all

json.キー名 インスタンス変数 do |変数名|
  json.カラム名1 変数名.カラム名1
  json.カラム名2 変数名.カラム名2
  ・
 ・
end

出力は {キー名:[ {カラム名1:値1,,,,,},{},{} ]}のようになります。

実例

@users = User.all

json.users @users do |user|
    json.id user.id
    json.name user.name
end

↓ ページにアクセスした結果(出力)

{"users":[{"id":1,"name":"test1"},{"id":3,"name":"test2"},{"id":4,"name":"test3"},{"id":5,"name":"test4"},{"id":6,"name":"test5"}]}


すべての行データJSONにする

対象のデータのIDを指定した場合など、必要なデータ行のみが含まれたデータをJSON形式に変換する方法にはattributesjson.merge!を使います。

attributesとは?

attributesとは、モデル(ActiveModel)に対して使えるメソッドの1つです。

モデル経由でテーブルの行データを取得する、find, find_by, firstなどのメソッドで取得したデータはオブジェクトになっています。

このデータにattributesメソッドを使うと、ハッシュ値に変換することができます。

#firstメソッドでデータを取得
irb(main):015:0> User.first
  User Load (1.0ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> #<User id: 1, name: "test1", age: 24, created_at: "2021-07-29 04:38:52.761212000 +0000", updated_at: "2021-07-29 04:38:52.761212000 +0000">


#attributesメソッドを使用
irb(main):014:0> User.first.attributes
  User Load (0.8ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> {"id"=>1, "name"=>"test1", "age"=>24, "created_at"=>Thu, 29 Jul 2021 04:38:52.761212000 UTC +00:00, "updated_at"=>Thu, 29 Jul 2021 04:38:52.761212000 UTC +00:00}

#<User id:1,,,>だったデータが、attributesを使ったことで、{"id"=>1,,,,}に変換されています。

※注意点

attributesは、allやwhereメソッドで取得したActiveRecord::Relationのデータには使えません。

インスタンス変数 = モデルクラスで取得したオブジェクトデータ.attributes

json.キー名 do
  json.merge! インスタンス変数
end

出力は {キー名:[ {カラム名1:値1,,,,,},{},{} ]}のようになります。

実例

@user = User.first.attributes

json.user do
  json.merge! @user
end

↓ ページにアクセスした結果(出力)

{"user":{"id":1,"name":"test1","age":24,"created_at":"2021-07-29T04:38:52.761Z","updated_at":"2021-07-29T04:38:52.761Z"}}


カラムを指定して行のデータを抜き出す

モデル経由で取得した行データのうち必要なカラムのみを指定してJSONに変換するにjson.extract!を使います。

インスタンス変数 = モデルクラスで取得したオブジェクトデータ

json.extract! インスタンス変数, :カラム名1, :カラム名2,,,


実例

@user = User.first

json.extract! @user, :id, :name, :age

↓ ページにアクセスした結果(出力)

{"id":1,"name":"test1","age":24}


パーシャルを使う

ビューのパーシャル(部分テンプレート)のように、.json.builderもパーシャルを作成することができます。

ビューのパーシャルと同じくファイル名の冒頭にアンダースコアを付けます。(例: _user.json.builder)

パーシャルを使う目的

メインの .json.builderファイルにはモデルを使ったデータの取得と、パーシャルの指定のみを記述します。

そして対象のパーシャルでJSONで表示するための処理を記述します。

こうすることで、指定するパーシャル名を変更するだけで、出力するJSONデータを変えることができます。


パーシャルの指定方法

パーシャルを指定するときは、allメソッドで取得したActiveRecord::Relationのデータと、findやfind_byで取得したオブジェクト型のデータで呼び出し方が異なります。

allメソッドで取得したデータの場合

クラスインスタンス = モデルクラス.all

json.array! クラスインスタンス, partial: 'ディレクトリ名/パーシャル名', as: :渡すデータのキー名
  • whereメソッドで取得したデータも使えます。
  • パーシャルファイルが同じ階層にある場合はディレクトリ名を省略できます。
  • アクセスするURIが一覧表示ページ(indexアクション)と一致する場合は、インスタンスの代入式を省略できます。

例: Usersコントローラのindexアクションに対応する /users.json にアクセスする場合は以下のようにも記述できます。

json.array! @users, partial: "user", as: :user

@usersが、@users = User.allになっています。



findやfind_byメソッドで取得したデータの場合

クラスインスタンス = モデルクラス.find(1)

json.partial! 'ディレクトリ名/パーシャル名', 渡すデータのキー名: クラスインスタンス
  • メソッドは一例です。
  • パーシャルファイルが同じ階層にある場合はディレクトリ名を省略できます。
  • アクセスするURIが一覧表示ページ(showアクション)と一致する場合は、インスタンスの代入式を省略できます。

例: Usersコントローラのshowアクションに対応する /users/:id.json にアクセスする場合は以下のようにも記述できます。

json.partial! "users/user", user: @user

@usersが、@users = User.allになっています。


パーシャル使用の実例

usersディレクトリ配下に index.json.jbuilderと、show.json.jbuiilderを作成し、パーシャルとして_user.json.jbuilderを呼び出す場合は次のようになります。

rails g scaffold user を実行すると自動生成される状態です)

ディレクトリ構造

app
 |
 |- views
      |
      |- users
           |- index.json.jbuilder
           |- index.html.erb
           |- show.json.jbuilder
           |- show.html.erb
           |- _user.json.jbuilder

index.json.jbuilder

json.array! @users, partial: "users/user", as: :user

show.json.jbuiilder

json.partial! "users/user", user: @user

_user.json.jbuilder

json.extract! user, :id, :name, :age, :created_at, :updated_at
json.url user_url(user, format: :json)

json.extract!で、カラムを指定してデータを取得しています。

json.url user_url(user, format: :json)は、キー名が「url」値が「user_url(user, format: :json)」のデータです。

"url":"http://localhost:3001/users/3.json" のようなキーと値のセットになります。


出力結果

上記設定で、それぞれ、/users.jsonと、/users/1.jsonにアクセスするとWEBページの表示は次のようになります。

▼/users.json

[{"id":1,"name":"test1","age":24,"created_at":"2021-07-29T04:38:52.761Z","updated_at":"2021-07-29T04:38:52.761Z","url":"http://localhost:3001/users/1.json"},{"id":3,"name":"test2","age":25,"created_at":"2021-07-30T23:18:28.177Z","updated_at":"2021-07-30T23:18:28.177Z","url":"http://localhost:3001/users/3.json"},{"id":4,"name":"test3","age":28,"created_at":"2021-07-30T23:19:35.242Z","updated_at":"2021-07-30T23:19:35.242Z","url":"http://localhost:3001/users/4.json"},{"id":5,"name":"test4","age":30,"created_at":"2021-07-30T23:19:44.976Z","updated_at":"2021-07-30T23:19:44.976Z","url":"http://localhost:3001/users/5.json"},{"id":6,"name":"test5","age":32,"created_at":"2021-07-30T23:19:54.558Z","updated_at":"2021-07-30T23:19:54.558Z","url":"http://localhost:3001/users/6.json"}]


▼ /users/1.json

{"id":1,"name":"test1","age":24,"created_at":"2021-07-29T04:38:52.761Z","updated_at":"2021-07-29T04:38:52.761Z","url":"http://localhost:3001/users/1.json"}


user_url(user, format: :json)とは?

user_url(user, format: :json)とは、ルートのprefixがurlとなるページに、userデータを渡し、末尾に.jsonを付けたURLを返すものです。

大きく次の3つに分解して考えることができます。

  • _urlヘルパ
  • データを渡す
  • パスの末尾を指定(format:)

_urlヘルパ

_urlはRailsのヘルパの1つで、prefixにつけると完全なURLを返します。

prefixとは各ルーティングに割り当てられた名前のようなものです。rails routesコマンドで確認できます。

# rails routes
Prefix    Verb   URI Pattern                Controller#Action
users     GET    /users(.:format)           users#index
new_user  GET    /users/new(.:format)       users#new
edit_user GET    /users/:id/edit(.:format)  users#edit
user      GET    /users/:id(.:format)       users#show
                                         

user_urlは、users#showに対応するルートであることがわかります。


データを渡す

showアクションやeidtアクションで開くページは、idというパラメータを個別に渡す必要があります。

_urlヘルパにパラメータのデータを渡すには引数を使います。渡し方は2パターンあります。

  • 直接渡す 例:user_url(2)
  • データが格納されたインスタンスを渡す 例:user_url(@user), user_url(User.first)


パスの末尾を指定(format:)

ルーティングには、/users(.:format) のように、対象のURIの末尾に、.:formatが記載されています。

_urlヘルパの引数に format: :〇〇 として値を渡すと、URIの :formatに指定した値が入ります。

例えば、users_url(format: :json) だと、/users.json というURIに対応する絶対パスが返ります。


タイトルとURLをコピーしました