Ruby: JSON.parseのあまり知られていない機能(翻訳)
RubyのJSON.parse
が文字列キーのハッシュを返すことにうんざりしたことのある方、シンボルキーのハッシュが欲しい方、本記事は皆さんのためにあります。
こんなときは、Rails開発者ならおそらくご存知のHash
のdeep_symbolize_keys
メソッドが使えます。特に理想の世界では、データ構造が以下のようなハッシュになっています。
require 'json'
json = <<~JSON
{
foo: {
bar:
"baz"
}
}
JSON
> JSON.parse(json)
=> { "foo" => { "bar" => "baz" } }
> JSON.parse(json).deep_symbolize_keys
=> { foo: { bar: "baz" } }
おそらくこれで足りると思いますが、私たちはRailsの世界だけで生きているわけではないので、いつもActive Supportが助けに来てくれるとは限りません。さらに、JSONのペイロードが単純なハッシュ的構造になっているとも限りません。有効なJSONフォーマットにはこんなものもあるのです。まずは1つ目。
> JSON.dump("")
#=> "\"\""
> JSON.dump(nil)
#=> "null"
> JSON.dump([{ foo: { bar: "baz" } }])
#=> "[{\"foo\":{\"bar\":\"baz\"}}]"
deep_symoblize_keys
の技は、そのままでは上のどれにも通用しないという事実を思い知らされます。型を再帰的にチェックするトリッキーなアルゴリズムを書いて、可能な場合にはsymbolize_keys
やdeep_symbolize_keys
を適用するなら別ですが。
それでは、Ruby自身が提供するJSONクラスのドキュメントをみっちり読んでみましょう。
json = <<~JSON
{
foo: {
bar:
"baz"
}
}
JSON
> JSON.parse(json, symbolize_names: true)
=> { foo: { bar: "baz" } }
ハッシュのコレクションを持つArrayではどうなるかも調べてみましょう。
> JSON.parse("[{\"foo\":{\"bar\":\"baz\"}}]", symbolize_names: true)
=> [{ foo: { bar: "baz" } }]
完璧です。
私がこの機能をどうやって見つけたかですか?少し前に、PostgreSQLのjsonカラムにデータを保存する読み取り用モデルで作業していたときのことです。ご存知のようにこのデータは自動的にシリアライズ/デシリアライズされます。つまりjsonカラムから読み込んだ結果は、以下のように文字列キーを持つデータ構造になります。
# 以前の実装
class FancyModel < ActiveRecord::Base
end
> FancyModel.last.my_json_column
=> [{"foo" => { "bar" => "baz" } }]
これは自分にとって相当不便でしたので、シンボルで値にアクセスできる信頼性の高い方法が欲しくなりました。とくにこの場合は、ハッシュを要素に含む配列だったのです。ドキュメントを少し読み進めた結果、以下のようなカスタムのシリアライザを書くことに成功しました。
class FancyModel < ActiveRecord::Base
class SymbolizedSerializer
def self.load(json)
JSON.parse(json, symbolize_names: true)
end
def self.dump(data)
JSON.dump(data)
end
end
serialize :my_json_column, SymbolizedSerializer
end
> FancyModel.last.my_json_column
#=> [{foo: { bar: "baz" } }]
RubyのJSONクラスに隠れているこの機能はあまり知られていないように思います。本記事がお役に立ちましたら、お気軽にシェアしてください。
お知らせ
ARKADEMY.DEVに参加してArkencyのトップクラス教育プログラムコースにアクセスしましょう!「Railsアーキテクトマスタークラス」「アンチ"IF"コース」「忙しいプログラマーのためのブログ執筆コース」「Async Remoteコース」「TDD動画クラス」「ドメイン駆動Rails動画コース」以外にもさまざまなコースが新設中です。
概要
原著者の許諾を得て翻訳・公開いたします。
参考: 週刊Railsウォッチ20211110