Ruby 如何将字符串对象转换为哈希对象?
我有一个看起来像散列的字符串:Ruby 如何将字符串对象转换为哈希对象?,ruby,Ruby,我有一个看起来像散列的字符串: "{ :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 =&g
"{ :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中如何键入有效哈希的所有属性。快速脏方法
eval("{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }")
但它具有严重的安全隐患。它执行所传递的任何内容,您必须有110%的把握(如中所示,至少没有用户输入)它将只包含格式正确的散列或来自外层空间的意外错误/可怕生物可能会突然出现。通过调用
Hash#inspect
创建的字符串可以通过调用eval
返回到散列中。但是,这要求哈希中的所有对象都是相同的
如果我从hash{:a=>Object.new}
开始,那么它的字符串表示形式是“{:a=>}”
,我不能使用eval
将其转换回hash,因为
不是有效的Ruby语法
但是,如果散列中的所有内容都是字符串、符号、数字和数组,那么它应该可以工作,因为这些都有有效的Ruby语法的字符串表示。可能是YAML.load?我是在为此编写了一行程序之后提出这个问题的,所以我分享了我的代码,以防它对某人有所帮助。适用于只有一级深度和可能的空值(但不是零)的字符串,如: 代码是:
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]}
我更喜欢滥用ActiveSupport::JSON。他们的方法是将哈希转换为yaml,然后加载它。不幸的是,到yaml的转换并不简单,您可能想借用它,就好像您的项目中还没有AS一样 我们还必须将任何符号转换为常规字符串键,因为符号在JSON中不合适 但是,它无法处理包含日期字符串的散列(我们的日期字符串最终没有被字符串包围,这就是最大的问题所在): 字符串=“{”上次请求时间:2011-12-28 23:00:00 UTC}”
ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>',':'))
当它试图解析日期值时,将导致无效的JSON字符串错误
希望您能就如何处理这个问题提供一些建议这个简短的小片段就可以了,但我看不出它能与嵌套哈希一起工作。不过我觉得它很可爱
STRING.gsub(/[{}:]/,'').split(', ').map{|h| h1,h2 = h.split('=>'); {h1 => h2}}.reduce(:merge)
台阶
1.我消除了“{”、“}”和“:”
2.我在绳子上找到一个“,”的地方劈开
3.每当它找到一个“=>”时,我就会拆分使用拆分创建的每个子字符串。然后,我创建一个散列,散列的两边我刚刚分开。
4.剩下的是一组散列,然后将它们合并在一起
输入示例:“{:用户id=>11,:博客id=>2,:评论id=>1}”
结果输出:{“user\u id”=>“11”、“blog\u id”=>“2”、“comment\u id”=>“1”}对于不同的字符串,您可以不使用危险的
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('=>', ':')
在rails 4.1中工作,支持不带引号的符号{:a=>b'} 只需将其添加到initializers文件夹:
class String
def to_hash_object
JSON.parse(self.gsub(/:([a-zA-z]+)/,'"\\1"').gsub('=>', ': ')).symbolize_keys
end
end
到目前为止,解决方案涵盖了一些情况,但遗漏了一些(见下文)。以下是我在更彻底(安全)转换方面的尝试。我知道这个解决方案不能处理的一种情况是由奇数字符组成的单字符符号,但允许使用字符。例如
{:>=>:\s]+)\s*=>/,“\1”\2”=>”)
#将对象字符串编号转换为带引号的字符串
ruby\u hash\u text.gsub!(/([{,]\s*)([0-9]+\.?[0-9]*)\s*=>/,'\1'\2'=>'))
#将对象值符号转换为引号字符串
ruby\u hash\u text.gsub!(/([{,]\s*)(“+?”[0-9]+\.?[0-9]*)\s*=>\s*:([^,}\s]+\s*)/,“\1\2=>”\3“)
#将数组值符号转换为引号字符串
ruby\u hash\u text.gsub!(/([\[,]\s*):([^,\]\s]+)/,'\1'\2')
#将对象字符串对象值分隔符转换为冒号分隔符
ruby\u hash\u text.gsub!(/([{,]\s*)(“+?”[0-9]+\.?[0-9]*)\s*=>,“\1\2:”)
放入ruby\u哈希\u文本
放置JSON.parse(ruby\u hash\u文本)
下面是关于其他解决方案的一些注释
- and的解决方案使用了
,这对我来说太可怕了(正如Toms正确指出的那样)eval
- 如果您的哈希没有符号或数字键,则可以使用
- 如果没有带引号的字符串与符号混合,则
- 如果您的符号未使用所有允许的字符(),则可以使用
- 只要不混合使用符号和带引号的字符串,就可以使用
请考虑此解决方案。库+规格:< /P> 文件:
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
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
我也有同样的问题。我在Redis中存储了一个散列。检索该散列时,它是一个字符串。出于安全考虑,我不想调用
eval(str)
。我的解决方案是将散列保存为json字符串,而不是ruby散列字符串。如果您有选择,使用json会更容易
redis.set(key, ruby_hash.to_json)
JSON.parse(redis.get(key))
TL;DR:useto_json
和json.parse
我构建了一个gem,首先使用ruby\u parser
gem检查散列是否安全。然后,它才应用eval
你可以把它当作
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)
中的测试为您提供了我为确保eval安全而测试的更多示例。遇到了需要使用eval()的类似问题 在我的情况下,我从API中提取一些数据并将其写入本地文件,然后能够从文件中提取数据并使用哈希 我使用IO.read()将文件内容读入一个变量。在本例中,IO.read()将其创建为字符串 然后使用eval()将字符串转换为哈希
read_handler = IO.read("Path/To/File.json")
puts read_handler.kind_of?(String) # Returns TRUE
a = eval(read_handler)
puts a.kind_of?(Hash) # Returns TRUE
puts a["Enter Hash Here"] # Returns Key => Values
puts a["Enter Hash Here"].length # Returns number of key value pairs
puts a["Enter Hash Here"]["Enter Key Here"] # Returns associated value
还有一点要提的是,IO是文件的祖先。因此,如果您愿意,您也可以使用File.read来代替。我认为eval会在这里做一些事情。让我先测试一下。我想我发布这个问题太早了。:)哦,是的,把它传给eval。:)我随身带着一把光剑。我可以照顾那些生物和虫子。:)使用eval可能会很危险据我的老师说,我们在这里。Eval接受任何ruby c
read_handler = IO.read("Path/To/File.json")
puts read_handler.kind_of?(String) # Returns TRUE
a = eval(read_handler)
puts a.kind_of?(Hash) # Returns TRUE
puts a["Enter Hash Here"] # Returns Key => Values
puts a["Enter Hash Here"].length # Returns number of key value pairs
puts a["Enter Hash Here"]["Enter Key Here"] # Returns associated value