2012-01-03 164 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