AWSの環境構築内容を履歴管理したくて、AWS CLIで出力したJSONをバージョン管理ツールにコミットして差分確認することにしたのですが、AWS CLIで出力されるJSONの配列などの順序が毎回必ずしも同じではなく、変更が無いのにDiffが出ることがありました。
そこで、JSONをソート出来ればと思ったのですが、いろいろ検索してもJSONを良い感じにソートできるLinuxコマンドが見つからなく、JSONをソートするRubyをチャカっと書いて、その場しのぎをしたのでメモ。
JSONの要素のキーと値の両方を文字列として接続して、その文字列でソートして、同じ内容のJSONであれば表記の順序がまちまちでも同じ出力結果になるように出来たと思います。
もっと良い方法など気づいた事がありましたらコメント頂けると幸いです。
sort_json.rb
#!<パス>/ruby
# -*- coding: utf-8 -*-
require 'json'
def sortHash(h)
str = ''
new_hash = Hash.new
h.sort.to_h.each do |k, v|
new_v, s = sortObject(v)
new_hash[k] = new_v
str += k.to_s + s
end
return new_hash, str
end
def sortArray(a)
tmp_hash = Hash.new
a.each do |v|
new_v, s = sortObject(v)
tmp_hash[s] = new_v
end
str = ''
new_array = []
tmp_hash.sort.to_h.each do |k, v|
str += k
new_array << v
end
return new_array, str
end
def sortObject(o)
if o.class == Hash
return sortHash(o)
elsif o.class == Array
return sortArray(o)
else
return o, o.to_s
end
end
begin
hash = JSON.load(STDIN.read)
hash, str = sortObject(hash)
json_str = JSON.pretty_generate(hash)
puts json_str
end
<実行例>
$ echo '{"c":1, "b":2, "a":3}' | ./sort_json.rb
{
"a": 3,
"b": 2,
"c": 1
}
↑ keyを優先してソート
$ echo '[{"a":3}, {"a":2}, {"a":1}]' | ./sort_json.rb
[
{
"a": 1
},
{
"a": 2
},
{
"a": 3
}
]
↑ keyが同じなら値でソート
$ echo '[{"a":1, "c":1}, {"b":[{"x":"z"},{"x":9}], "a":1}, {"a":1}]' | ./sort_json.rb
[
{
"a": 1
},
{
"a": 1,
"b": [
{
"x": 9
},
{
"x": "z"
}
]
},
{
"a": 1,
"c": 1
}
]
↑ keyが有ったり無かったりの複合
<AWS CLIでの例>
$ aws ec2 describe-security-groups --output json | <パス>/sort_json.rb > security-groups.json
<参考にした記事>
RubyでJSONを扱うときに便利な関数まとめ