info以下のコードなどでDateTimeとかの時間をちょっといじってあるので整合性が合わないのもあるので注意

例えば以下のようにオブジェクトをJSONに出力をするとする

require "json"
require "date"

class Sample
  def initialize(params)
    @name = params[:name]
    @date = params[:date]
  end

  def to_json(*)
    {
      name: @name,
      date: @date
    }.to_json
  end
end

puts JSON.generate(
  Sample.new(name: "hoge", date: DateTime.now)
)

出力したJSONをファイルに保管しておいて以下のようにロードする

require "json"

class Sample
  def initialize(params)
    @name = params[:name]
    @date = params[:date]
  end

  def to_json(*)
    {
      name: @name,
      date: @date
    }.to_json
  end
end

sample = JSON.load_file("sample.json")

はい。ここで問題!変数sampleのクラスはなんでしょう?答えはかんたんで普通にHashです。

そこで今回の件!こうやってパースしたりした際にそういうオブジェクトに変換してほしいよねっていうお話なのですがちゃんとそういう仕組みが用意されているのでご紹介しますw

JSONを出力する側

require "json"
require "date"

class Sample
  attr_reader :name, :date

  def initialize(params)
    @name = params[:name]
    @date = params[:date]
  end

  def to_json(*)
    {
      JSON.create_id => self.class.name,
      name: @name,
      date: @date
    }.to_json
  end
end

puts JSON.generate(
  Sample.new(name: "hoge", date: DateTime.now)
)

要点は1つのみto_jsonで出力する際にJSON.create_idを設定しておくこと。それだけ。ということでこれで出力すると

{
  "json_class": "Sample",
  "name": "hoge",
  "date": "2025-06-30T00:00:00+09:00"
}

というような結果になる。でこれをパースする祭にSampleクラスに変換されてほしいよねって言う話なのでそこもやる

出力したJSONを利用する側

require "json"

class Sample
  attr_reader :name, :date

  def initialize(params)
    @name = params[:name]
    @date = params[:date]
  end

  def self.json_create(object)
    self.new(name: object["name"], date: object["date"])
  end
end

sample = JSON.load_file("sample.json", create_additions: true)
pp sample

# OUTPUT
# #<Sample:0x00007d20eee7e8c0 @date="2025-06-30T00:00:00+09:00", @name="hoge">

要点は以下の2つ

  • json_createのクラスメソッドを定義する。そこでJSONをパースしたものからオブジェクトを生成するようにする
  • load_file(or parse)にcreate_additionsを指定する

ということなんだが上記のソース中のコメントに記載してる結果を見ればわかるが@dateがDateTimeなどに変換されてなくてただの文字列になってるよね。そこもなんとかしようかと↓

DateTimeとかも変換する方法

require "json"
require "json/add/core"
require "date"

class Sample
  attr_reader :name, :date

  def initialize(params)
    @name = params[:name]
    @date = params[:date]
  end

  def to_json(*)
    {
      JSON.create_id => self.class.name,
      name: @name,
      date: @date
    }.to_json
  end
end

puts JSON.generate(
  Sample.new(name: "hoge", date: DateTime.now)
)

require "json/add/coreを追加しただけ。これを実行すると

{
  "json_class": "Sample",
  "name": "hoge",
  "date": {
    "json_class": "DateTime",
    "y": 2025,
    "m": 6,
    "d": 30,
    "H": 2,
    "M": 18,
    "S": 6,
    "of": "3/8",
    "sg": 2299161.0
  }
}

というようなJSONが出力される。でこのJSONを解析するには

require "json"
require "json/add/core"

class Sample
  attr_reader :name, :date

  def initialize(params)
    @name = params[:name]
    @date = params[:date]
  end

  def self.json_create(object)
    self.new(name: object["name"], date: object["date"])
  end
end

sample = JSON.load_file("sample.json", create_additions: true)
pp sample

# OUTPUT
# #<Sample:0x000073c5a9a2f3b8
# @date= #<DateTime: 2025-06-30T00:00:00+09:00 (省略)>,
# @name="hoge">

生成側と同じくrequire json/add/coreを追加しただけ。今回はDateTimeだけを変換しているのでjson/add/date_timeでも可能。必要だと思うものを指定すればいいと思われる(詳しくは以下のURL参照)

https://docs.ruby-lang.org/ja/latest/library/json=2fadd=2fcore.html

まとめ

要点をまとめるとRubyからJSONを出力しそれを利用する場合にオブジェクトに変換したりして欲しい場合には

  • 出力するJSONにJSON.create_idを設定する。
  • パースする側でオブジェクトに変換するjson_createクラスメソッドを定義する。あとcreate_additionsも設定するのを忘れずに
  • DateTimeとかも変換してほしいならrequire json/add/coreなどの拡張ライブラリを使用する

以上!でもRuby特有な機能だと思うのでどうなんだろなってのはちょっと思うけど内部的になんかやる時にJSONで出力してそれを利用する場合とかには使えるかもね(JSON APIとかには×だと思う)

余談:create_additionsオプションについて

generateとparse(or load_file)両方にこのオプションあるがgenerateのcreate_additionsはデフォルトがtrueなのに対しparse(or load_file)ではデフォルトがfalseになっている模様なので(ry