【Ruby入門説明書】jsonについて解説
はじめに
Rubyでプログラムを作っている中で、天気予報や為替など、Webサービスの情報を利用することがあります。
そういったWebサービスの情報を受け取る(送付する)には、特定の書式に従ったファイルでやりとりすることになるでしょう。その特定の書式の1つが、JSON形式という書式です。
JSON形式は、複雑なデータ構造をテキストファイルで保存でき、サイズが小さくて軽いため、インターネット上をやりとりするのに都合が良く、いろいろなプログラム言語やシステムで採用されています。
そんなJSON形式のファイルやデータ構造を扱うことのできるjsonモジュールについて、詳しく解説していきましょう。
JSONとは?
Webシステムでは、情報を外部から取り込んだり、外部へ提供したりすることがとても多く、まったく違う(他社の)システムともやりとりを行う必要が出てきます。
そういったときに利用されるのが、JSON形式のファイルです。
JSONは「JavaScript Object Notation」の略称で、その名の通り、そもそもはJavaScriptで情報の保存や送受信で使われていたファイルです。
一定の形式に則って記載されたテキストファイルで、複雑なデータ構造を軽量なファイルで扱うことができるとして、Rubyを始め、他の多くの言語でも採用されています。
JSON形式の書式
JSON形式の書式は、ハッシュによく似ており、キーと値で構成されています。基本的な書式は、以下のオブジェクトと配列の2つだけで、この書式を組み合わせてデータを格納します。
オブジェクト
{
キー: 値
}
配列
[オブジェクト, オブジェクト, …]具体例を挙げておきましょう。
{
“num” : 2,
“data”:[
{
“name”:”ryosuke”,
“param”: {
“height”: 168,
“weight”: 53
}
},
{
“name”:”hikaru”,
“param”: {
“height”: 178,
“weight”: 63
}
}
]
}
以降はこのJSONデータを利用していきますので、「json_test.json」というファイル名で、プログラムファイルを保存しているフォルダ(ディレクトリ)へ保存しておいてください。
なお、jsonデータを見た瞬間に「ハッシュと同じように見えた」人もいるかもしれません。
じつのところ、JSON形式のデータをRubyで扱う場合、基本的にはハッシュへ変換して扱うことになります(配列の場合もあります)。そのため、ハッシュとよく似ているというイメージは、JSON形式を扱う上で好都合と言えます。
RubyでJSONを使う
では、ここから、RubyでJSON形式のデータを扱う方法を説明していきましょう。 RubyでJSON形式のデータを扱うためには、jsonモジュールを利用します。 なお、jsonモジュールを使う場合、requireメソッドで取り込む必要がありますので、注意してください。
require “json”
jsonモジュールには数多くのメソッドが定義されていますが、特によく使うメソッドを紹介します。
メソッド | 説明 |
---|---|
load | JSONファイルからJSON形式のデータを読み込んで、ハッシュや配列へ変換する ハッシュにするか配列にするかは、データの内容によります |
dump | ハッシュや配列をJSON形式へ変換してファイルへ保存します |
parse | JSON形式の文字列を読み込んで、ハッシュや配列へ変換する ハッシュにするか配列にするかは、データの内容によります |
generate to_json |
ハッシュや配列をJSON形式の文字列へ変換する |
これらのメソッドについて、詳しく解説していきましょう。
jsonファイルの読み書き(load、dump)
JSON形式のファイルを読み書きするには、loadメソッドとdumpメソッドを使います。
それぞれについて、少し詳しく説明しましょう。
jsonファイルの読み込みを行うloadメソッド
loadメソッドの構文は、以下の通りです。
JSON.load(ファイルオブジェクト)
JSON.load(ファイルオブジェクト) do |file|
end
引数には、ファイルオブジェクトを指定します。ファイルオブジェクトは、Fileクラスのopenメソッドを使って取得します。
Fileクラスのopenメソッドとcloseメソッド
File.open(ファイル名, モード)
「ファイル名」には、対象ファイルのフォルダ\ファイル名の文字列を記載します。
「モード」には、ファイルの処理方法に合わせて、以下の6種類のいずれかを指定します。なお、デフォルト値として”r”が設定されています。
モード | 内容 |
---|---|
r | ファイルを読み取りモードで開きます ファイルがなければエラーが発生します |
w | ファイルを新規書き込みモードで開きます ファイルがなければ作成します。すでに存在している場合はファイルを空にして開きます |
a | ファイルを追加書き込みモードで開きます ファイルがなければ作成します。すでに存在している場合はそのまま開きます 書き込みの処理を行うと、ファイルの末尾に追記します |
r+ | ファイルを読み書き両用モードで開きます ファイルがなければエラーが発生します 書き込み処理を行うと先頭から書き込みます |
w+ | ファイルを読み書き両用モードで開きます ファイルがなければ作成します。すでに存在している場合はファイルを空にして開きます 書き込み処理を行うと先頭からから書き込みます |
a+ | ファイルを読み書き両用モードで開きます ファイルがなければ作成します。すでに存在している場合はそのまま開きます 読み込みは先頭から行いますが、書き込みはファイルの末尾に追記します |
ブロックを使わなければ、戻り値として、ファイルオブジェクトが返されます。しかし、その場合は、closeメソッドを使用しなければ不具合を発生させてしまいますので、注意が必要です。
ファイルオブジェクト.close
ブロックを使う場合は、ブロック引数にファイルオブジェクトが格納され、ブロック終了とともに自動的にcloseメソッドが呼ばれます。そのため、特別な理由がない限り、ブロックを使ったopenメソッドを利用することをおすすめします。
なお、Fileクラスはrequireすることなく、標準で利用できます。
JSONモジュールのloadメソッドは、ファイルオブジェクトからデータを読み込んで、ハッシュオブジェクトへ変換したものを戻り値として返します。
先ほど保存したjson_test.jsonファイルを読み込んでみましょう。
require “json”
File.open(“json_test.json”) do |file|
hash = JSON.load(file)
p(hash)
end
(結果)
{"num"=>2, "data"=>[{"name"=>"ryosuke", "param"=>{"height"=>168, "weight"=>53}}, {"name"=>"hikaru", "param"=>{"height"=>178, "weight"=>63}}]}
jsonファイルの書き込みを行うdumpメソッド
次にファイルへの書き込みを行うdumpメソッドです。
こちらも、ファイルオブジェクトを使って書き込みを行いますので、Fileクラスのopenメソッドであらかじめファイルオブジェクトを取得しておく必要があります。
JSON.dump(ハッシュオブジェクト, ファイルオブジェクト)
ハッシュオブジェクトはファイルに格納したいデータすべてです。jsonデータがすでに書き込まれているファイルに対して、新たな要素を追記するような使い方はできませんので、注意が必要です。(そのため、openメソッドのモードは”w”や”w+”を指定することになるでしょう)
追記したい場合は、あらかじめファイルから読み込んだデータに追記し、それを改めて上書きするといった処理になります。
require “json”
# 読み込み
hash = {}
File.open(“json_test.json”) do |file|
hash = JSON.load(file)
end
p(hash)
# データ追加
add_data = {
“name”=>”yuri”,
“param” => {
“height” => 158,
“weight” => 43
}
}
hash[“data”].append(add_data) # データ追加
hash[“num”] += 1
puts(“— 追加 ——“)
# 書き込み
File.open(“json_test.json”, “w”) do |file|
JSON.dump(hash, file)
end
# 再度読み込み
File.open(“json_test.json”) do |file|
hash = JSON.load(file)
end
p(hash)
(結果)
[bash]{“num”=>2, “data”=>[{“name”=>”ryosuke”, “param”=>{“height”=>168, “weight”=>53}}, {“name”=>”hikaru”, “param”=>{“height”=>178, “weight”=>63}}]}
— 追加 ——
{“num”=>3, “data”=>[{“name”=>”ryosuke”, “param”=>{“height”=>168, “weight”=>53}}, {“name”=>”hikaru”, “param”=>{“height”=>178, “weight”=>63}}, {“name”=>”yuri”, “param”=>{“height”=>158, “weight”=>43}}]}
[/bash]
JSON形式の文字列とハッシュの変換(parse、generate、to_json)
じつは、外部のWebサービスからJSON形式のデータを取得したとき、文字列として返されてくることがあります。(特定の文字列の中に、JSON形式で書かれている部分が含まれている場合など) また、逆に、JSON形式ではなく、JSON形式を含む文字列で渡さなければ、正常に処理してもらえないWebサービスもあります。 その場合、JSON形式の文字列とハッシュを相互に変換してやる必要があるわけです。
JSON形式の文字列からハッシュオブジェクト(配列)を取得する(parse)
JSON形式の文字列からハッシュオブジェクト(配列)を取得するには、parseメソッドを使用します。
JSON.parse(JSON形式の文字列)
引数にJSON形式の文字列を渡すことで、ハッシュオブジェクト(配列)が返ってきます。
require “json”
# JSON形式の文字列
json_str= ‘{“records”:[{“name”:”ryosuke”,”points”:{“math”:80,”eng”:10}},
{“name”:”yuri”,”points”:{“math”:20,”eng”:90}}]}’
# 変換
hash = JSON.parse(json_str)
p(hash)
(結果)
[bash]{“records”=>[{“name”=>”ryosuke”, “points”=>{“math”=>80, “eng”=>10}}, {“name”=>”yuri”, “points”=>{“math”=>20, “eng”=>90}}]}
[/bash]
ハッシュオブジェクト(配列)からJSON形式の文字列からを取得する(generate、to_json)
処理が完了したハッシュオブジェクト(配列)をJSON形式の文字列を変換するには、generateメソッドを使います。
JSON.generate(オブジェクト)
オブジェクトは、JSON形式の文字列へ変換したいハッシュもしくは配列を渡すと、戻り値として、JSON形式の文字列が返ってきます。
require "json"
# データ
hash = {“records” =>[
{
“name”=>”ryosuke”,
“points”=>
{
“math”=>80,
“eng”=>10
}
},
{
“name”=>”yuri”,
“points”=>
{
“math”=>20,
“eng”=>90
}
}]
}
# 変換
json_str = JSON.generate(hash)
p(json_str)
(結果)
[bash]“{\”records\”:[{\”name\”:\”ryosuke\”,\”points\”:{\”math\”:80,\”eng\”:10}},{\”name\”:\”yuri\”,\”points\”:{\”math\”:20,\”eng\”:90}}]}”
# 「\」は、pメソッドでの出力が””で囲まれた形で出力されるため、JSON形式内の「”」を正常に出力するための記号です。
[/bash]
また、generateメソッドではなく、ハッシュや配列のメソッドであるto_jsonメソッドを使っても、JSON形式の文字列を取得することができます。
オブジェクト.to_json
※to_jsonメソッドは、JSONモジュールのメソッドではなく、ハッシュや配列のメソッドです。
require “json”
# データ
hash = {“records”=>[
{
“name”=>”ryosuke”,
“points”=>
{
“math”=>80,
“eng”=>10
}
},
{
“name”=>”yuri”,
“points”=>
{
“math”=>20,
“eng”=>90
}
}]
}
# 変換
json_str = hash.to_json
p(json_str)
結果は、generateメソッドを使った場合と同じになります。
問題「JSON形式のデータの集計」
以下のJSON形式のデータを読み込み、平均雨量を正しくしてください。
rains = ‘{“雨量”:{ “8月1日”:20,”;8月2日”:2,”8月3日”:20,”8月4日”:0,”8月5日”:70,”8月6日”:90,”8月7日”:10, “8月8日”:28,”8月9日”:15,”8月10日”:15,”8月11日”:36,”8月12日”:22,”8月13日”:0,”8月14日”:89, “8月15日”:15,”8月16日”:8,”8月17日”:20,”8月18日”:0,”8月19日”:70,”8月20日”:0,”8月21日”:1, “8月22日”:3,”8月23日”:5,”8月24日”:0,”8月25日”:40,”8月26日”:0,”8月27日”:10,”8月28日”:9, “8月29日”:5,”8月30日”:0,”8月31日”:1}, “平均雨量”:0}’
まとめ
jsonモジュールについて、説明しました。
JSON形式は、数多くのシステムで採用されており、外部のサービスを利用する場合には対応が必須とも言えるものです。
ただし、特別に難しいものではありません。基本的にはハッシュと同じ形式ですので、ハッシュと合わせて改めて学習を進めていただければ、理解もスムーズに進むことでしょう。
なお、学習サイトなどではなかなか難しいですが、JSON形式についての歴史的背景や、実際に使われているサービスなども知ることができれば、より理解が深まります。
プログラミングスクールなど対面のサービスであれば、経験豊富な講師の方から、そういった点も教えてもらえますので、興味があれば、利用してみれば効果的でしょう。
・JSON形式はWebシステムでは一般的に使われているデータ形式
・Rubyでjsonモジュールを使うためには、requireが必要
・jsonモジュールは、Fileクラスで開いたファイルに対する処理を行う
・JSONファイルへの書き込みは、追記はできない。(毎回すべて上書き)
・ハッシュオブジェクトからJSON形式の文字列への変換は、generateメソッドかto_jsonメソッドを使う
問題の答え
require “json”
# データ
rains = ‘{“雨量”:{ “8月1日”:20,”;8月2日”:2,”8月3日”:20,”8月4日”:0,”8月5日”:70,”8月6日”:90,”8月7日”:10, “8月8日”:28,”8月9日”:15,”8月10日”:15,”8月11日”:36,”8月12日”:22,”8月13日”:0,”8月14日”:89, “8月15日”:15,”8月16日”:8,”8月17日”:20,”8月18日”:0,”8月19日”:70,”8月20日”:0,”8月21日”:1, “8月22日”:3,”8月23日”:5,”8月24日”:0,”8月25日”:40,”8月26日”:0,”8月27日”:10,”8月28日”:9, “8月29日”:5,”8月30日”:0,”8月31日”:1}, “平均雨量”:0}’
# ハッシュへ変換
hash = JSON.parse(rains)
# 平均雨量算出
sum = hash[“雨量”].values.inject do |sum, value|
sum + value
end
ave = sum / hash[“雨量”].values.size
hash[“平均雨量”] = ave
# JSON形式文字列へ変換
rains = hash.to_json
p(rains)