String 객체를 Hash 객체로 어떻게 변환합니까?
해시처럼 보이는 문자열이 있습니다.
"{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }"
해시를 어떻게 제거합니까? 처럼:
{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }
문자열은 중첩 깊이를 가질 수 있습니다. Ruby에서 유효한 해시를 입력하는 방법에 대한 모든 속성이 있습니다.
호출하여 작성된 문자열은 호출 Hash#inspect하여 해시로 다시 전환 될 수 있습니다 eval. 그러나 이것은 해시의 모든 객체에 대해 동일해야합니다.
hash로 시작하면 {:a => Object.new}문자열 표현은 "{:a=>#<Object:0x7f66b65cf4d0>}"이며 유효한 Ruby 구문이 아니기 eval때문에 문자열을 해시로 되돌릴 수 없습니다 #<Object:0x7f66b65cf4d0>.
그러나 해시에있는 모든 것이 문자열, 기호, 숫자 및 배열이면 유효한 Ruby 구문 인 문자열 표현이 있으므로 작동해야합니다.
다른 문자열의 경우 위험한 eval방법 을 사용하지 않고 할 수 있습니다 .
hash_as_string = "{\"0\"=>{\"answer\"=>\"1\", \"value\"=>\"No\"}, \"1\"=>{\"answer\"=>\"2\", \"value\"=>\"Yes\"}, \"2\"=>{\"answer\"=>\"3\", \"value\"=>\"No\"}, \"3\"=>{\"answer\"=>\"4\", \"value\"=>\"1\"}, \"4\"=>{\"value\"=>\"2\"}, \"5\"=>{\"value\"=>\"3\"}, \"6\"=>{\"value\"=>\"4\"}}"
JSON.parse hash_as_string.gsub('=>', ':')
빠르고 더러운 방법은
eval("{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }")
그러나 보안에 심각한 영향을 미칩니다.
전달 된 모든 것을 실행하면 110 % 확신해야합니다 (적어도 어느 곳에도 사용자 입력이 없어야 함) 제대로 구성된 해시 만 포함하거나 외부 공간에서 예기치 않은 버그 / 끔찍한 생물이 팝업되기 시작할 수 있습니다.
이 짧은 스 니펫은 그것을 할 것이지만 중첩 된 해시와 함께 작동하는 것을 볼 수 없습니다. 그래도 꽤 귀엽다고 생각합니다
STRING.gsub(/[{}:]/,'').split(', ').map{|h| h1,h2 = h.split('=>'); {h1 => h2}}.reduce(:merge)
단계 1. '{', '}'및 ':'를 제거합니다. 2. ','를 찾을 때마다 문자열을 분할합니다. 3. 분할로 생성 된 각 하위 문자열을 찾을 때마다 분할합니다. '=>'. 그런 다음 방금 나눈 해시의 양면으로 해시를 만듭니다. 4. 해시 배열이 남은 다음 병합합니다.
입력 예 : "{: user_id => 11, : blog_id => 2, : comment_id => 1}"결과 출력 : { "user_id"=> "11", "blog_id"=> "2", "comment_id"= > "1"}
아마 YAML.load?
지금까지 솔루션은 일부 사례를 다루었지만 일부 사례는 누락되었습니다 (아래 참조). 보다 철저한 (안전한) 변환을위한 나의 시도는 다음과 같습니다. 나는이 솔루션이 홀수이지만 허용되는 문자로 구성된 단일 문자 기호를 처리하지 못하는 경우를 알고 있습니다. 예를 들어 {:> => :<}유효한 루비 해시입니다.
이 코드를 github에도 넣었 습니다 . 이 코드는 테스트 문자열로 시작하여 모든 변환을 수행합니다.
require 'json'
# Example ruby hash string which exercises all of the permutations of position and type
# See http://json.org/
ruby_hash_text='{"alpha"=>{"first second > third"=>"first second > third", "after comma > foo"=>:symbolvalue, "another after comma > foo"=>10}, "bravo"=>{:symbol=>:symbolvalue, :aftercomma=>10, :anotheraftercomma=>"first second > third"}, "charlie"=>{1=>10, 2=>"first second > third", 3=>:symbolvalue}, "delta"=>["first second > third", "after comma > foo"], "echo"=>[:symbol, :aftercomma], "foxtrot"=>[1, 2]}'
puts ruby_hash_text
# Transform object string symbols to quoted strings
ruby_hash_text.gsub!(/([{,]\s*):([^>\s]+)\s*=>/, '\1"\2"=>')
# Transform object string numbers to quoted strings
ruby_hash_text.gsub!(/([{,]\s*)([0-9]+\.?[0-9]*)\s*=>/, '\1"\2"=>')
# Transform object value symbols to quotes strings
ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>\s*:([^,}\s]+\s*)/, '\1\2=>"\3"')
# Transform array value symbols to quotes strings
ruby_hash_text.gsub!(/([\[,]\s*):([^,\]\s]+)/, '\1"\2"')
# Transform object string object value delimiter to colon delimiter
ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>/, '\1\2:')
puts ruby_hash_text
puts JSON.parse(ruby_hash_text)
여기 다른 솔루션에 대한 참고 사항이 있습니다.
- @ Ken Bloom 과 @Toms Mikoss 의 솔루션
eval은 너무 무섭습니다 (Toms가 올바르게 지적했듯이). - @zolter 의 솔루션은 사용자의 해시 어떤 기호 나 숫자 키가없는 경우 작동합니다.
- @jackquack's solution works if there are no quoted strings mixed in with the symbols.
- @Eugene's solution works if your symbols don't use all the allowed characters (symbol literals have a broader set of allowed characters).
- @Pablo's solution works as long as you don't have a mix of symbols and quoted strings.
I had the same problem. I was storing a hash in Redis. When retrieving that hash, it was a string. I didn't want to call eval(str) because of security concerns. My solution was to save the hash as a json string instead of a ruby hash string. If you have the option, using json is easier.
redis.set(key, ruby_hash.to_json)
JSON.parse(redis.get(key))
TL;DR: use to_json and JSON.parse
I prefer to abuse ActiveSupport::JSON. Their approach is to convert the hash to yaml and then load it. Unfortunately the conversion to yaml isn't simple and you'd probably want to borrow it from AS if you don't have AS in your project already.
We also have to convert any symbols into regular string-keys as symbols aren't appropriate in JSON.
However, its unable to handle hashes that have a date string in them (our date strings end up not being surrounded by strings, which is where the big issue comes in):
string = '{'last_request_at' : 2011-12-28 23:00:00 UTC }' ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>', ' : '))
Would result in an invalid JSON string error when it tries to parse the date value.
Would love any suggestions on how to handle this case
works in rails 4.1 and support symbols without quotes {:a => 'b'}
just add this to initializers folder:
class String
def to_hash_object
JSON.parse(self.gsub(/:([a-zA-z]+)/,'"\\1"').gsub('=>', ': ')).symbolize_keys
end
end
I built a gem hash_parser that first checks if a hash is safe or not using ruby_parser gem. Only then, it applies the eval.
You can use it as
require 'hash_parser'
# this executes successfully
a = "{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' },
:key_b => { :key_1b => 'value_1b' } }"
p HashParser.new.safe_load(a)
# this throws a HashParser::BadHash exception
a = "{ :key_a => system('ls') }"
p HashParser.new.safe_load(a)
The tests in https://github.com/bibstha/ruby_hash_parser/blob/master/test/test_hash_parser.rb give you more examples of the things I've tested to make sure eval is safe.
I came to this question after writing a one-liner for this purpose, so I share my code in case it helps somebody. Works for a string with only one level depth and possible empty values (but not nil), like:
"{ :key_a => 'value_a', :key_b => 'value_b', :key_c => '' }"
The code is:
the_string = '...'
the_hash = Hash.new
the_string[1..-2].split(/, /).each {|entry| entryMap=entry.split(/=>/); value_str = entryMap[1]; the_hash[entryMap[0].strip[1..-1].to_sym] = value_str.nil? ? "" : value_str.strip[1..-2]}
Please consider this solution. Library+spec:
File: lib/ext/hash/from_string.rb:
require "json"
module Ext
module Hash
module ClassMethods
# Build a new object from string representation.
#
# from_string('{"name"=>"Joe"}')
#
# @param s [String]
# @return [Hash]
def from_string(s)
s.gsub!(/(?<!\\)"=>nil/, '":null')
s.gsub!(/(?<!\\)"=>/, '":')
JSON.parse(s)
end
end
end
end
class Hash #:nodoc:
extend Ext::Hash::ClassMethods
end
File: spec/lib/ext/hash/from_string_spec.rb:
require "ext/hash/from_string"
describe "Hash.from_string" do
it "generally works" do
[
# Basic cases.
['{"x"=>"y"}', {"x" => "y"}],
['{"is"=>true}', {"is" => true}],
['{"is"=>false}', {"is" => false}],
['{"is"=>nil}', {"is" => nil}],
['{"a"=>{"b"=>"c","ar":[1,2]}}', {"a" => {"b" => "c", "ar" => [1, 2]}}],
['{"id"=>34030, "users"=>[14105]}', {"id" => 34030, "users" => [14105]}],
# Tricky cases.
['{"data"=>"{\"x\"=>\"y\"}"}', {"data" => "{\"x\"=>\"y\"}"}], # Value is a `Hash#inspect` string which must be preserved.
].each do |input, expected|
output = Hash.from_string(input)
expect([input, output]).to eq [input, expected]
end
end # it
end
참고URL : https://stackoverflow.com/questions/1667630/how-do-i-convert-a-string-object-into-a-hash-object
'IT' 카테고리의 다른 글
| reStructuredText 도구 지원 (0) | 2020.07.03 |
|---|---|
| 설치된 모든 node.js 모듈 목록을 인쇄하십시오. (0) | 2020.07.03 |
| 배쉬 : 주어진 상대 경로를 검색 (0) | 2020.07.03 |
| 월말 계산 (0) | 2020.07.03 |
| 여러 값과 같은 MySQL (0) | 2020.07.03 |