我正在尝试构建一个API包装器gem,并且在从API返回的JSON中将哈希键转换为更多Rubyish格式时遇到了问题。
JSON包含多层嵌套,包括哈希和数组。我想要做的是递归地将所有键转换为snake_case以便于使用。
这是我到目前为止所得到的:
def convert_hash_keys(value)
return value if (not value.is_a?(Array) and not value.is_a?(Hash))
result = value.inject({}) do |new, (key, value)|
new[to_snake_case(key.to_s).to_sym] = convert_hash_keys(value)
new
end
result
end
上面调用此方法将字符串转换为snake_case:
def to_snake_case(string)
string.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
理想情况下,结果类似于以下内容:
hash = {:HashKey => {:NestedHashKey => [{:Key => "value"}]}}
convert_hash_keys(hash)
# => {:hash_key => {:nested_hash_key => [{:key => "value"}]}}
我得到的递归错误,我试过的这种解决方案的每个版本都不会将符号转换为超出第一级别,或者过度使用并尝试转换整个哈希值,包括值。
尝试在helper类中解决所有这些问题,而不是在可能的情况下修改实际的Hash和String函数。
提前谢谢。
答案 0 :(得分:38)
您需要单独处理Array和Hash。而且,如果您使用的是Rails,则可以使用underscore
代替您的自制to_snake_case
。首先是帮助减少噪音的小帮手:
def underscore_key(k)
k.to_s.underscore.to_sym
# Or, if you're not in Rails:
# to_snake_case(k.to_s).to_sym
end
如果您的哈希值具有非符号或字符串的键,则可以适当修改underscore_key
。
如果你有一个数组,那么你只想递归地将convert_hash_keys
应用于数组的每个元素;如果您有哈希,则需要使用underscore_key
修复密钥并将convert_hash_keys
应用于每个值;如果你还有别的东西,那么你想要通过原状:
def convert_hash_keys(value)
case value
when Array
value.map { |v| convert_hash_keys(v) }
# or `value.map(&method(:convert_hash_keys))`
when Hash
Hash[value.map { |k, v| [underscore_key(k), convert_hash_keys(v)] }]
else
value
end
end
答案 1 :(得分:27)
如果您使用 Rails :
哈希示例:camelCase to snake_case :
hash = { camelCase: 'value1', changeMe: 'value2' }
hash.transform_keys { |key| key.to_s.underscore }
# => { "camel_case" => "value1", "change_me" => "value2" }
源: http://apidock.com/rails/v4.0.2/Hash/transform_keys
对于嵌套属性,请使用deep_transform_keys而不是transform_keys,例如:
hash = { camelCase: 'value1', changeMe: { hereToo: { andMe: 'thanks' } } }
hash.deep_transform_keys { |key| key.to_s.underscore }
# => {"camel_case"=>"value1", "change_me"=>{"here_too"=>{"and_me"=>"thanks"}}}
答案 2 :(得分:3)
'mu太短'所接受的答案已被转换为宝石,futurechimp的Plissken:
https://github.com/futurechimp/plissken/blob/master/lib/plissken/ext/hash/to_snake_keys.rb
这似乎应该在Rails之外工作,因为包含了下划线功能。
答案 3 :(得分:1)
使用deep_transform_keys进行递归转换。
transform_keys仅将其转换为高级
hash = { camelCase: 'value1', changeMe: {nestedMe: 'value2'} }
hash.transform_keys { |key| key.to_s.underscore }
# => { "camel_case" => "value1", "change_me" => {nestedMe: 'value2'} }
deep_transform_keys会更深入,并转换所有嵌套的哈希。
hash = { camelCase: 'value1', changeMe: {nestedMe: 'value2'} }
hash.deep_transform_keys { |key| key.to_s.underscore }
# => { "camel_case" => "value1", "change_me" => {nested_me: 'value2'} }
答案 4 :(得分:0)
如果您正在使用active_support库,则可以使用deep_transform_keys!像这样:
hash.deep_transform_keys! do |key|
k = key.to_s.snakecase rescue key
k.to_sym rescue key
end
答案 5 :(得分:0)
我使用以下简短形式:
hash.transform_keys(&:underscore)
而且,正如@Shanaka Kuruwita所指出的,要深刻地改变所有嵌套的哈希:
hash.deep_transform_keys(&:underscore)
答案 6 :(得分:0)
这对对象的 camelCase
和 snake_case
深度嵌套键都有效,这对 JSON API 非常有用:
def camelize_keys(object)
deep_transform_keys_in_object!(object) { |key| key.to_s.camelize(:lower) }
end
def snakecase_keys(object)
deep_transform_keys_in_object!(object) { |key| key.to_s.underscore.to_sym }
end
def deep_transform_keys_in_object!(object, &block)
case object
when Hash
object.keys.each do |key|
value = object.delete(key)
object[yield(key)] = deep_transform_keys_in_object!(value, &block)
end
object
when Array
object.map! { |e| deep_transform_keys_in_object!(e, &block) }
else
object
end
end