Eu tenho uma string que se parece com um hash:
"{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }"
Como faço para obter um Hash? gostar:
{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }
A cadeia pode ter qualquer profundidade de aninhamento. Possui todas as propriedades de como um Hash válido é digitado no Ruby.
Respostas:
A cadeia criada pela chamada
Hash#inspect
pode ser transformada novamente em um hash chamandoeval
-a. No entanto, isso exige o mesmo para todos os objetos no hash.Se eu começar com o hash
{:a => Object.new}
, sua representação de string será"{:a=>#<Object:0x7f66b65cf4d0>}"
e não posso usáeval
-lo para transformá-lo novamente em um hash, porque#<Object:0x7f66b65cf4d0>
não é uma sintaxe válida do Ruby.No entanto, se tudo o que estiver no hash for seqüências de caracteres, símbolos, números e matrizes, deve funcionar, porque elas possuem representações de sequência de caracteres que são válidas na sintaxe Ruby.
fonte
eval
atualizada como um hash, certificando-me de que a instrução acima seja válida para essa string.eval
no lugar errado é uma enorme falha de segurança. Qualquer coisa dentro da string será avaliada. Então, imagine se em uma API alguém injetourm -fr
Para cadeias diferentes, você pode fazer isso sem usar o
eval
método perigoso :fonte
JSON.parse(hash_as_string.gsub("=>", ":").gsub(":nil,", ":null,"))
O método rápido e sujo seria
Mas tem implicações graves de segurança.
Ele executa o que quer que seja passado, você deve ter 110% de certeza (como, pelo menos, nenhuma entrada do usuário em qualquer lugar do caminho) que conteria apenas hashes adequadamente formados ou bugs inesperados / criaturas horríveis do espaço sideral podem começar a aparecer.
fonte
Talvez YAML.load?
fonte
Este pequeno trecho curto fará isso, mas não consigo vê-lo trabalhando com um hash aninhado. Eu acho que é bem fofo
Etapas 1. Eu elimino o '{', '}' e o ':' 2. Divido a string onde quer que encontre um ',' 3. Divido cada uma das substrings criadas com a divisão, sempre que encontrar a '=>'. Em seguida, crio um hash com os dois lados do hash que acabei de separar. 4. Fico com uma série de hashes que depois mesclo.
EXEMPLO DE ENTRADA: "{: user_id => 11,: blog_id => 2,: comment_id => 1}" RESULTADO DA SAÍDA: {"user_id" => "11", "blog_id" => "2", "comment_id" = > "1"}
fonte
{}:
caracteres dos valores dentro do hash string?Até agora, as soluções abrangem alguns casos, mas faltam alguns (veja abaixo). Aqui está minha tentativa de uma conversão mais completa (segura). Conheço um caso de canto que esta solução não lida com símbolos de caracteres únicos compostos por caracteres ímpares, mas permitidos. Por exemplo,
{:> => :<}
é um hash ruby válido.Eu coloquei esse código no github também . Esse código começa com uma sequência de teste para exercitar todas as conversões
Aqui estão algumas notas sobre as outras soluções aqui
eval
que é assustador demais para mim (como Toms corretamente aponta).fonte
:nil
para:null
lidar com isso em particular estranheza.Eu tive o mesmo problema. Eu estava armazenando um hash em Redis. Ao recuperar esse hash, era uma string. Não quis ligar
eval(str)
por questões de segurança. Minha solução foi salvar o hash como uma string json, em vez de uma string ruby hash. Se você tiver a opção, usar o json é mais fácil.TL; DR: uso
to_json
eJSON.parse
fonte
to_json
eJSON.parse
Eu prefiro abusar do ActiveSupport :: JSON. A abordagem deles é converter o hash em yaml e carregá-lo. Infelizmente, a conversão para yaml não é simples e você provavelmente deseja emprestá-la do AS se ainda não o tiver no seu projeto.
Também precisamos converter quaisquer símbolos em chaves de string regulares, pois os símbolos não são apropriados no JSON.
No entanto, é incapaz de lidar com hashes que contenham uma string de data (nossas strings de data acabam não sendo cercadas por strings, que é onde entra o grande problema):
string = '{' last_request_at ': 28-12-2011 23:00:00 UTC}'
ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>', ' : '))
Resultaria em um erro de cadeia JSON inválido ao tentar analisar o valor da data.
Gostaria de sugestões sobre como lidar com este caso
fonte
ActiveSupport::JSON.decode(response.body, symbolize_keys: true)
trabalha nos trilhos 4.1 e suporta símbolos sem aspas {: a => 'b'}
basta adicionar isso à pasta inicializadores:
fonte
Criei uma gema hash_parser que primeiro verifica se um hash é seguro ou não está usando
ruby_parser
gemas. Só então, aplica-se oeval
.Você pode usá-lo como
Os testes em https://github.com/bibstha/ruby_hash_parser/blob/master/test/test_hash_parser.rb oferecem mais exemplos das coisas que testei para garantir que a avaliação seja segura.
fonte
Por favor, considere esta solução. Biblioteca + especificações:
Arquivo
lib/ext/hash/from_string.rb
:Arquivo
spec/lib/ext/hash/from_string_spec.rb
:fonte
it "generally works"
mas não necessariamente? Eu seria mais detalhado nesses testes.it "converts strings to object" { expect('...').to eql ... }
it "supports nested objects" { expect('...').to eql ... }
Cheguei a essa pergunta depois de escrever uma linha para esse fim, então compartilho meu código para o caso de ajudar alguém. Funciona para uma string com apenas um nível de profundidade e possíveis valores vazios (mas não nulos), como:
O código é:
fonte
Deparamos com um problema semelhante que precisava usar o eval ().
Minha situação, eu estava puxando alguns dados de uma API e gravando-os em um arquivo localmente. Em seguida, é possível extrair os dados do arquivo e usar o Hash.
Eu usei IO.read () para ler o conteúdo do arquivo em uma variável. Nesse caso, IO.read () o cria como uma String.
Em seguida, use eval () para converter a string em um Hash.
Apenas para mencionar que o IO é um ancestral do File. Portanto, você também pode usar o File.read, se desejar.
fonte