你可以使用PostgreSQL的
json
类型并保持顺序。如果要利用
jsonb
的许多性能优势,则会失去本地顺序保留。
以下是一种保留顺序的方法,通过在每个键中注入数字索引:
class OrderedHashSerializer < ActiveRecord::Coders::JSON
class << self
def dump(obj)
ActiveSupport::JSON.encode(
dump_transform(obj)
)
end
def load(json)
json = ActiveSupport::JSON.decode(json) if json.is_a?(String)
load_transform(json)
end
private
def dump_transform(obj)
obj.transform_keys.with_index do |key, index|
"#{index + 1}_#{key}"
end
end
def load_transform(hash)
hash
&.sort { |item, next_item| item.first.to_i <=> next_item.first.to_i }
&.map { |key, value| format_item(key, value) }
&.to_h
end
def format_item(key, value)
[
key.gsub(/^\d+_/, '').to_sym,
value.in?([nil, true]) ? value : value.try(:to_sym) || value
]
end
end
end
注意,这将破坏在 SQL 查询中使用嵌入式 JSON 数据的能力,因为所有键名都会被污染。但如果您需要保留顺序而不是需要 JSON 查询,这是一种解决方案。(尽管必须承认,在那种情况下,json
类型开始看起来非常好)
测试看起来像:
describe OrderedHashSerializer do
describe '#load' do
subject(:invoke) { described_class.load(data) }
let(:data) do
{
'1_error' => 'checksum_failure',
'2_parent' => nil,
'22_last_item' => 'omega',
'3_code' => 'service_server_failure',
'4_demographics': { age: %w[29], 'flavor' => %w[cherry vanilla rhubarb] }
}.to_json
end
it 'formats data properly when loading it from database' do
is_expected.to eq(
error: :checksum_failure,
parent: nil,
last_item: :omega,
code: :service_server_failure,
demographics: { 'age' => ["29"], 'flavor' => %w[cherry vanilla rhubarb] },
)
end
it 'preserves intended key order' do
expect(invoke.keys.last).to eq :last_item
end
end
describe '#dump' do
subject(:invoke) { described_class.dump(data) }
let(:data) do
{
'error' => 'checksum_failure',
'parent' => nil,
'code' => 'service_server_failure',
demographics: { age: %w[65], 'flavor' => %w[cherry vanilla rhubarb] },
'last_item' => 'omega'
}
end
it 'prefixes keys with the numbers, in order' do
is_expected.to eq(
{
"1_error" => :checksum_failure,
"2_parent" => nil,
"3_code" => :service_server_failure,
"4_demographics" => { age: %w[65], flavor: %w[cherry vanilla rhubarb] },
"5_last_item" => :omega
}.to_json
)
end
end
end
@mu is too short
),因此无法“接受”。我不想接受自己的答案,而其他答案(尽管有喜欢和好信息)并不能像@mu is too short
那样解决我的问题。以下是他的答案链接。基本上,您应该在jsonb列中使用数组([{str_fee: 6}, {eva_fee: 11}, ...]
)来保存顺序。 - skplunkerin