LoginSignup
3
3

More than 5 years have passed since last update.

Ruby 1.8系でsort by keyなJSONを出力する

Last updated at Posted at 2013-02-08

Ruby 1.8系のHashは順列を保持しないため、Hashを含むデータをJSONで出力しようとすると、実行するたびに並び順の違うJSONが出力されてしまいます。しかしそれだと、テストがやりづらかったり、本来同一内容のファイルが違う内容になってしまったり、いろいろ面倒です。
諸事情ありRuby 1.9系にバージョンを上げたくなかったので、Ruby 1.8系のままsort by keyなJSONをdump出来るようにしてみました。

サンプルコード

手軽にデータを準備...ということで、YAMLをJSONに変換するコードで解説。

yaml2json.rb
#!/usr/bin/env ruby -w
require 'rubygems'
require 'json/pure'
require 'yaml'

module JSON::Pure::Generator::GeneratorMethods::Hash
  remove_method :json_transform
  def json_transform(state)
    delim = ','
    delim << state.object_nl
    result = '{'
    result << state.object_nl
    depth = state.depth += 1
    first = true
    indent = !state.object_nl.empty?
    keys.sort.each { |k|
      result << delim unless first
      result << state.indent * depth if indent
      result << k.to_s.to_json(state)
      result << state.space_before
      result << ':'
      result << state.space
      result << self[k].to_json(state)
      first = false
    }
    depth = state.depth -= 1
    result << state.object_nl
    result << state.indent * depth if indent if indent
    result << '}'
    result
  end
end

puts JSON.dump(YAML.load(ARGF.read))

ポイント

jsonモジュールはデフォルト動作はCで書かれた拡張版で動作しますが、json/pureを明示的に指定することで、Rubyで書かれたpure版を使う事が出来ます。Rubyであれば既存のモジュールのメソッドを上書きすることが出来るので、Hashをまさにeachしているこいつのコードを書き換えてsortするようにしてしまいます。先にremove_methodしているのは、これをしないで再定義すると"warning: method redefined; discarding old json_transform"と警告が出てしまうので、お行儀を良くするために。

これでJSON中に含まれる全てのHashはキーでソートされるので、実行毎に出力結果が変わることが無くなります。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3