RubyのHash型をjsonモジュールのto_jsonを使うときに気をつけたいこと

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

サーモン大好き横山です。

今回はrubyのjsonモジュールの to_json メソッドで気になる挙動があったので紹介します。

用途

そもそもこちらのメソッドは、RubyのHashやArray等のデータ型を、json形式の文字列に変換してくれるメソッドです。

コード

#!/usr/bin/env ruby
# demo.rb

require 'json'

data = {
  "num" => 1,
  "string" => "abc",
  "array" => [1, 2, 3],
  "hash" => {"key1" => "value1", "key2" => "value2"} 
}

puts data.to_json

結果

$ ruby demo.rb
{"num":1,"string":"abc","array":[1,2,3],"hash":{"key1":"value1","key2":"value2"}}

jq を使うと見やすく表示できます。

$ ruby demo.rb | jq .
{
  "num": 1,
  "string": "abc",
  "array": [
    1,
    2,
    3
  ],
  "hash": {
    "key1": "value1",
    "key2": "value2"
  }
}

to_json メソッドの気になる挙動

表題についてですが、Hash型のキーで シンボル文字列 が同名のものが存在すると同一キーのハッシュとしてjsonを生成してしまいます。 これは、RFC4627 2.2 Objects の以下の記述に反しています。

The names within an object SHOULD be unique. 1

コード

#!/usr/bin/env ruby
# demo2.rb

require 'json'

data = {
  key: 1,
  "key" => 2
}

puts data.to_json

実行結果

$ ruby demo2.rb
{"key":1,"key":2}

問題点

このままjqに渡すと片方のデータのみ表示してしまいます。

実行結果

$ ruby demo2.rb | jq .
{
  "key": 2
}

demo2.rb のHashの要素を入れ替えた demo3.rb で実行するとこうなります。

コード

#!/usr/bin/env ruby
# demo3.rb

require 'json'

data = {
  "key" => 2,
  key: 1
}

puts data.to_json

実行結果

$ ruby demo3.rb
{"key":2,"key":1}

$ ruby demo3.rb | jq .
{
  "key": 1
}

ハッシュの後ろのデータが最終的にjqで出力されます。

まとめ

以上のことから、Hash型のキーで シンボル文字列 が同名のものが存在するときの to_json の挙動は重複キーを含まれるjsonが生成されます。

jqを通した場合、ハッシュの後ろのデータが最終的にjqで出力されます。

他のサービスではどういう挙動をするのかわかりませんので、なるべくHash型をjsonに変換する時は気をつけて to_json メソッドを呼び出すようにコードを書いたほうがいいですね。