Каков наилучший способ преобразования пары значений json-форматированного ключа в рубиновый хеш с символом в качестве ключа?
Мне интересно, как лучше всего преобразовать пару ключей с форматированием json в рубиновый хеш с символом в качестве ключа:
Пример:
{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }
==>
{ :user=>{ :name => 'foo', :age =>'40', :location=>{ :city => 'bar', :state=>'ca' } } }
Можно ли использовать вспомогательный метод?
Ответы
Ответ 1
используя json gem при анализе json-строки, которую вы можете передать в опции symbolize_names. См. Здесь: http://flori.github.com/json/doc/index.html (смотрите под разделом)
например:
>> s ="{\"akey\":\"one\",\"bkey\":\"two\"}"
>> JSON.parse(s,:symbolize_names => true)
=> {:akey=>"one", :bkey=>"two"}
Ответ 2
Левентикс, спасибо за ваш ответ.
Метод Marshal.load(Marshal.dump(h)), вероятно, обладает наибольшей целостностью различных методов, поскольку он рекурсивно сохраняет исходные типы ключей.
Это важно, если у вас есть вложенный хеш с сочетанием строк и символьных ключей, и вы хотите сохранить этот микс при декодировании (например, это может произойти, если ваш хэш содержит ваши собственные пользовательские объекты в дополнение к очень сложным/вложенные сторонние объекты, ключи которых вы не можете манипулировать/конвертировать по любой причине, например ограничение по времени проекта).
например:.
h = {
:youtube => {
:search => 'daffy', # nested symbol key
'history' => ['goofy', 'mickey'] # nested string key
}
}
Метод 1: JSON.parse - символизирует все ключи рекурсивно = > Не сохраняет оригинальный микс
JSON.parse( h.to_json, {:symbolize_names => true} )
=> { :youtube => { :search=> "daffy", :history => ["goofy", "mickey"] } }
Метод 2: ActiveSupport:: JSON.decode - символизирует только клавиши верхнего уровня = > Не сохраняет оригинальный микс
ActiveSupport::JSON.decode( ActiveSupport::JSON.encode(h) ).symbolize_keys
=> { :youtube => { "search" => "daffy", "history" => ["goofy", "mickey"] } }
Метод 3: Marshal.load - сохраняет исходную комбинацию строк/символов во вложенных ключах. ИДЕАЛЬНОЕ!
Marshal.load( Marshal.dump(h) )
=> { :youtube => { :search => "daffy", "history" => ["goofy", "mickey"] } }
Если есть недостаток, о котором я не знаю, я бы подумал, что способ 3 - это путь.
Приветствия
Ответ 3
Нет ничего встроенного, чтобы сделать трюк, но не слишком сложно написать код, чтобы сделать это, используя жемчужину JSON. Существует метод symbolize_keys
, встроенный в Rails, если вы его используете, но это не символизирует ключи рекурсивно, как вам нужно.
require 'json'
def json_to_sym_hash(json)
json.gsub!('\'', '"')
parsed = JSON.parse(json)
symbolize_keys(parsed)
end
def symbolize_keys(hash)
hash.inject({}){|new_hash, key_value|
key, value = key_value
value = symbolize_keys(value) if value.is_a?(Hash)
new_hash[key.to_sym] = value
new_hash
}
end
Как сказал Левентикс, жемчужина JSON обрабатывает только строки с двойными кавычками (что технически корректно - JSON следует форматировать с двойными кавычками). Этот бит кода очистит это, прежде чем пытаться его проанализировать.
Ответ 4
Рекурсивный метод:
require 'json'
def JSON.parse(source, opts = {})
r = JSON.parser.new(source, opts).parse
r = keys_to_symbol(r) if opts[:symbolize_names]
return r
end
def keys_to_symbol(h)
new_hash = {}
h.each do |k,v|
if v.class == String || v.class == Fixnum || v.class == Float
new_hash[k.to_sym] = v
elsif v.class == Hash
new_hash[k.to_sym] = keys_to_symbol(v)
elsif v.class == Array
new_hash[k.to_sym] = keys_to_symbol_array(v)
else
raise ArgumentError, "Type not supported: #{v.class}"
end
end
return new_hash
end
def keys_to_symbol_array(array)
new_array = []
array.each do |i|
if i.class == Hash
new_array << keys_to_symbol(i)
elsif i.class == Array
new_array << keys_to_symbol_array(i)
else
new_array << i
end
end
return new_array
end
Ответ 5
Конечно, есть json gem, но это обрабатывает только двойные кавычки.
Ответ 6
Другой способ справиться с этим - использовать сериализацию/десериализацию YAML, которая также сохраняет формат ключа:
YAML.load({test: {'test' => { ':test' => 5}}}.to_yaml)
=> {:test=>{"test"=>{":test"=>5}}}
Преимущество такого подхода представляется как формат, который лучше подходит для служб REST...