将 json 格式的键值对转换为以符号为键的 ruby​​ 哈希的最佳方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1732001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:57:41  来源:igfitidea点击:

what is the best way to convert a json formatted key value pair to ruby hash with symbol as key?

ruby-on-railsrubyjson

提问by ez.

I am wondering what is the best way to convert a json formatted key value pair to ruby hash with symbol as key: example:

我想知道将 json 格式的键值对转换为以符号为键的 ruby​​ 哈希的最佳方法是什么:示例:

{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }
==> 
{ :user=>{ :name => 'foo', :age =>'40', :location=>{ :city => 'bar', :state=>'ca' } } }

Is there a helper method can do this?

有没有辅助方法可以做到这一点?

回答by jai

using the json gem when parsing the json string you can pass in the symbolize_names option. See here: http://flori.github.com/json/doc/index.html(look under parse)

在解析 json 字符串时使用 json gem,您可以传入 Symbolic_names 选项。在这里看到:http: //flori.github.com/json/doc/index.html(在解析下看)

eg:

例如:

>> s ="{\"akey\":\"one\",\"bkey\":\"two\"}"
>> JSON.parse(s,:symbolize_names => true)
=> {:akey=>"one", :bkey=>"two"} 

回答by frank

Leventix, thank you for your answer.

Leventix,谢谢你的回答。

The Marshal.load(Marshal.dump(h))method probably has the most integrity of the various methods because it preserves the original key types recursively.

Marshal.load(Marshal.dump(H))方法可能有各种方法最诚信,因为它保留了原始密钥类型递归

This is important in case you have a nested hash with a mix of string and symbol keys and you want to preserve that mix upon decode (for instance, this could happen if your hash contains your own custom objects in addition to highly complex/nested third-party objects whose keys you cannot manipulate/convert for whatever reason, like a project time constraint).

如果您有一个包含字符串和符号键混合的嵌套散列并且您希望在解码时保留该混合(例如,如果您的散列包含您自己的自定义对象以及高度复杂/嵌套的第三个对象,这可能会发生) -party 对象,您无法出于任何原因操作/转换其密钥,例如项目时间限制)。

E.g.:

例如:

h = {
      :youtube => {
                    :search   => 'daffy',                 # nested symbol key
                    'history' => ['goofy', 'mickey']      # nested string key
                  }
    }

Method 1: JSON.parse - symbolizes all keys recursively => Does not preserve original mix

方法 1:JSON.parse - 递归地表示所有键 => 不保留原始混合

JSON.parse( h.to_json, {:symbolize_names => true} )
  => { :youtube => { :search=> "daffy", :history => ["goofy", "mickey"] } } 

Method 2: ActiveSupport::JSON.decode - symbolizes top-level keys only => Does not preserve original mix

方法 2:ActiveSupport::JSON.decode - 仅象征顶级键 => 不保留原始混合

ActiveSupport::JSON.decode( ActiveSupport::JSON.encode(h) ).symbolize_keys
  => { :youtube => { "search" => "daffy", "history" => ["goofy", "mickey"] } }

Method 3: Marshal.load - preserves original string/symbol mix in the nested keys. PERFECT!

方法 3:Marshal.load - 保留嵌套键中的原始字符串/符号组合。完美的!

Marshal.load( Marshal.dump(h) )
  => { :youtube => { :search => "daffy", "history" => ["goofy", "mickey"] } }

Unless there is a drawback that I'm unaware of, I'd think Method 3 is the way to go.

除非有我不知道的缺点,否则我认为方法 3 是可行的方法。

Cheers

干杯

回答by madlep

There isn't anything built in to do the trick, but it's not too hard to write the code to do it using the JSON gem. There is a symbolize_keysmethod built into Rails if you're using that, but that doesn't symbolize keys recursively like you need.

没有任何内置的东西来完成这个技巧,但是使用 JSON gem 编写代码来做到这一点并不难。symbolize_keys如果您正在使用它,Rails 中内置了一种方法,但这不会像您需要的那样递归地符号化键。

require 'json'

def json_to_sym_hash(json)
  json.gsub!('\'', '"')
  parsed = JSON.parse(json)
  symbolize_keys(parsed)
end

def symbolize_keys(hash)
  hash.inject({}){|new_hash, key_value|
    key, value = key_value
    value = symbolize_keys(value) if value.is_a?(Hash)
    new_hash[key.to_sym] = value
    new_hash
  }
end

As Leventix said, the JSON gem only handles double quoted strings (which is technically correct - JSON should be formatted with double quotes). This bit of code will clean that up before trying to parse it.

正如 Leventix 所说,JSON gem 只处理双引号字符串(这在技术上是正确的 - JSON 应该用双引号格式化)。这段代码将在尝试解析它之前清理它。

回答by Oel Roc

Recursive method:

递归方法:

require 'json'

def JSON.parse(source, opts = {})
  r = JSON.parser.new(source, opts).parse
  r = keys_to_symbol(r) if opts[:symbolize_names]
  return r
end

def keys_to_symbol(h)
  new_hash = {}
  h.each do |k,v|
    if v.class == String || v.class == Fixnum || v.class == Float
      new_hash[k.to_sym] = v
    elsif v.class == Hash
      new_hash[k.to_sym] = keys_to_symbol(v)
    elsif v.class == Array
      new_hash[k.to_sym] = keys_to_symbol_array(v)
    else
      raise ArgumentError, "Type not supported: #{v.class}"
    end
  end
  return new_hash
end

def keys_to_symbol_array(array)
  new_array = []
  array.each do |i|
    if i.class == Hash
      new_array << keys_to_symbol(i)
    elsif i.class == Array
      new_array << keys_to_symbol_array(i)
    else
      new_array << i
    end
  end
  return new_array
end

回答by Leventix

Of course, there is a json gem, but that handles only double quotes.

当然,有一个json gem,但它只处理双引号。

回答by bert bruynooghe

Another way to handle this is to use YAML serialization/deserialization, which also preserves the format of the key:

处理此问题的另一种方法是使用 YAML 序列化/反序列化,这也保留了密钥的格式:

YAML.load({test: {'test' => { ':test' => 5}}}.to_yaml) 
=> {:test=>{"test"=>{":test"=>5}}}

Benefit of this approach it seems like a format that is better suited for REST services...

这种方法的好处似乎是一种更适合 REST 服务的格式......

回答by Mario Ruiz

The most convenient way is by using the nice_hash gem: https://github.com/MarioRuiz/nice_hash

最方便的方法是使用 nice_hash gem:https: //github.com/MarioRuiz/nice_hash

require 'nice_hash'
my_str = "{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }"

# on my_hash will have the json as a hash
my_hash = my_str.json

# or you can filter and get what you want
vals = my_str.json(:age, :city)

# even you can access the keys like this:
puts my_hash._user._location._city
puts my_hash.user.location.city
puts my_hash[:user][:location][:city]