2012-01-03 7 views
19

Sto cercando di creare un gioiello di wrapper API e di avere problemi con la conversione delle chiavi di hash in un formato più Rubyish dal JSON restituito dall'API.Conversione di chiavi hash nidificate da CamelCase a snake_case in Ruby

Il JSON contiene più livelli di nidificazione, sia Hash che Array. Quello che voglio fare è convertire ricorsivamente tutte le chiavi in ​​snake_case per un uso più semplice.

Ecco quello che ho finora:

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 

Le suddette chiamate questo metodo per convertire le stringhe di 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 

Idealmente, il risultato sarebbe simile al seguente:

Sto ottenendo la ricorsione sbagliata, e ogni versione di questo tipo di soluzione che ho provato sia non converte i simboli oltre il primo livello, o va in mare e cerca di convertire l'intero hash, inclusi i valori.

Provare a risolvere tutto questo in una classe helper, anziché modificare le funzioni Hash e String effettive, se possibile.

Grazie in anticipo.

+0

Prima di fare qualsiasi altra cosa, "se (non ... e non ...)" è un posto perfetto per usare la legge di De Morgan. Dovresti scriverlo 'a meno che ... o ...'. – sawa

risposta

31

È necessario trattare separatamente Array e Hash. E, se sei in Rails, puoi usare underscore invece del tuo homebrew to_snake_case. Prima un po 'di aiuto per ridurre il rumore:

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 

Se le hash avranno chiavi che non sono simboli o stringhe, allora è possibile modificare underscore_key in modo appropriato.

Se si dispone di una matrice, è sufficiente applicare in modo ricorsivo convert_hash_keys a ciascun elemento della matrice; se si dispone di un hash, si desidera correggere le chiavi con underscore_key e applicare convert_hash_keys a ciascuno dei valori; se avete qualcosa di diverso allora si consiglia di passare attraverso intatta:

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

Funziona come un fascino. Grazie gentilmente Non lo sto facendo in Rails, ma credo che il codice che ho usato per 'to_snake_case' derivi dal metodo 'underscore' di Rails. –

+0

@Andrew: Il 'string.gsub (/ :: /, '/')' in 'to_snake_case' mi fa pensare che tu abbia ragione su dove' to_snake_case' proviene. Benvenuti in SO, godetevi il vostro soggiorno. –

+0

Non è necessario utilizzare Rails, solo ActiveSupport, che ci consente di selezionare la routine. 'require 'active_support/core_ext/string/inflections'' dovrebbe farlo:'' FooBar'.underscore => "foo_bar" '. –

17

se si utilizza Rails:

Esempio con hash: camelCase a snake_case:

hash = { camelCase: 'value1', changeMe: 'value2' } 

hash.transform_keys { |key| key.to_s.underscore } 
# => { "camel_case" => "value1", "change_me" => "value2" } 

fonte: http://apidock.com/rails/v4.0.2/Hash/transform_keys

Per gli attributi nidificati utilizzare deep_transform_keys invece di transform_keys, esempio:

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

fonte: http://apidock.com/rails/v4.2.7/Hash/deep_transform_keys

+0

questo trasforma solo il primo livello dell'hash, le chiavi di hash annidate sono ancora rimaste come un caso di cammello. – nayiaw

+1

Grazie per l'attenzione ho aggiornato la mia risposta. –

+0

Funziona come un incantesimo, grazie! – born4new

0

Se si utilizza la libreria active_support, è possibile utilizzare deep_transform_keys! Così:

hash.deep_transform_keys! do |key| 
    k = key.to_s.snakecase rescue key 
    k.to_sym rescue key 
end