2012-01-03 171 views
19

我试图构建一个API包装gem,并将哈希键从API返回的JSON转换为更多的Rubyish格式。将嵌套的哈希键从CamelCase转换为Ruby中的snake_case

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"}]}} 

我得到了递归错误,我试过这种解决方案的每一个版本不会将符号转换超出第一级,或者过度并尝试转换整个散列,包括值。

尝试在辅助类中解决所有这些问题,而不是修改实际的哈希和字符串函数(如果可能)。

预先感谢您。

+0

在做其他事情之前,'if(not ... and not ...)'是使用De Morgan定律的理想场所。你应该写下',除非......或......'。 – sawa 2012-01-03 01:39:01

回答

31

你需要分开处理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 
+0

工程就像一个魅力。非常感谢你。我没有在Rails中这样做,但我相信我用于'to_snake_case'的代码来自Rails的下划线方法。 – 2012-01-03 02:41:20

+0

@Andrew:'to_snake_case'中的'string.gsub(/ :: /,'/')'让我觉得你对'to_snake_case'的来源是正确的。欢迎来到SO,享受您的逗留。 – 2012-01-03 02:44:23

+0

没有必要使用Rails,只需使用ActiveSupport,它可以让我们挑选例程。 'require'active_support/core_ext/string/inflections''应该这样做:''FooBar'.underscore =>“foo_bar”'。 – 2012-01-03 02:47:24

17

如果使用的Rails

实施例与散列:驼峰到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"}}} 

来源:http://apidock.com/rails/v4.2.7/Hash/deep_transform_keys

+0

这只能转换哈希的第一级,嵌套哈希键仍然保留为骆驼大小写。 – nayiaw 2017-03-10 06:34:32

+1

感谢您的关注,我更新了我的答案。 – 2017-03-10 20:09:06

+0

工程就像一个魅力,谢谢! – born4new 2017-05-09 06:08:13

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