json 형식의 키 값 쌍을 기호를 키로 사용하는 루비 해시로 변환하는 가장 좋은 방법은 무엇입니까?


102

json 형식의 키 값 쌍을 기호를 키로 사용하는 루비 해시로 변환하는 가장 좋은 방법이 무엇인지 궁금합니다. 예 :

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

이 작업을 수행 할 수있는 도우미 메서드가 있습니까?


http://stackoverflow.com/a/43773159/1297435레일 4.1을 위해 이것을 시도하십시오
rails_id 2017 년

답변:


255

json 문자열을 구문 분석 할 때 json gem을 사용하여 symbolize_names 옵션을 전달할 수 있습니다. 여기를 참조하십시오 : http://flori.github.com/json/doc/index.html (구문 분석 아래 참조 )

예 :

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

4
참고로 Ruby 1.9에는이 라이브러리가 포함되어 있습니다.
Simon Perepelitsa 2012 년

이것은 예전에는 아니 었 :symbolize_keys습니까? 왜 그 이름이 바뀌 었습니까?
Lukas

5
@Lukas : symbolize_keysRails의 일입니다.
wyattisimo

: symbolize_names는 루비 것은 생각입니다
fatuhoku

19

Leventix, 귀하의 답변에 감사드립니다.

Marshal.load (Marshal.dump (H)) 원래 키 유형을 보존하기 때문에 방법은 아마 다양한 방법의 가장 무결성을 가지고 반복적으로 .

이는 문자열과 기호 키가 혼합 된 중첩 된 해시가 있고 디코딩시 해당 혼합을 보존하려는 경우에 중요합니다 (예를 들어, 해시에 고도로 복잡 / 중첩 된 세 번째 개체 외에 고유 한 사용자 지정 개체가 포함 된 경우 발생할 수 있음) -프로젝트 시간 제약과 같이 어떤 이유로 든 키를 조작 / 변환 할 수없는 파티 개체).

예 :

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

방법 1 : JSON.parse-모든 키를 재귀 적으로 상징 => 원본 믹스를 보존하지 않음

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

방법 2 : ActiveSupport :: JSON.decode-최상위 키만 상징 => 원본 믹스를 보존하지 않음

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

방법 3 : Marshal.load-중첩 된 키에서 원래 문자열 / 기호 혼합을 유지합니다. 완전한!

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

내가 알지 못하는 단점이 없다면 방법 3이 갈 길이라고 생각합니다.

건배


2
여기서 상대방을 제어 할 수 있다는 보장은 없으므로 JSON 형식을 고수해야한다고 생각합니다. 양쪽 모두를 완전히 제어 할 수 있다면 Marshal은 실제로 좋은 형식이지만 범용 직렬화에는 적합하지 않습니다.
chills42 2014 년

5

트릭을 수행하기 위해 내장 된 것은 없지만 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

Leventix가 말했듯이 JSON gem은 큰 따옴표로 묶인 문자열 만 처리합니다 (기술적으로 정확합니다-JSON은 큰 따옴표로 형식화되어야 함). 이 코드는 구문 분석을 시도하기 전에이를 정리합니다.


4

재귀 방법 :

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

1

물론 json gem 이 있지만 큰 따옴표 만 처리합니다.


madlep은 아래 말했듯이 - 당신은 JSON이 유효 할 것이라는 점을 알고 있다면 그게 당신이 필요로하는 모든입니다 (예를 들어, 당신은 스스로를 만들고있어!)
edavey

이것은 작동하지 않습니다. JSON.parse(JSON.generate([:a])) # => ["a"]
Justin L.

2
JSON은 기호를 나타낼 수 없기 때문입니다. Marshal.load(Marshal.dump([:a]))대신 사용할 수 있습니다 .
Leventix

1

이를 처리하는 또 다른 방법은 YAML 직렬화 / 역 직렬화를 사용하는 것입니다. 이는 키의 형식도 유지합니다.

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

이 접근 방식의 이점은 REST 서비스에 더 적합한 형식 인 것 같습니다.


사용자 입력이 YAML.load에 들어 가지 않도록하십시오 : tenderlovemaking.com/2013/02/06/yaml-f7u12.html
Rafe

@Rafe 2013 년의이 보안 구멍이 아직 해결되지 않았다는 뜻인가요?
bert bruynooghe

1
기호는 Ruby 2.2부터 GC됩니다. YAML.load임의의 객체를 직렬화하는 것을 의미합니다 (예 : 캐시). 제안 된 내용 YAML.safe_load은 블로그 게시물 몇 달 후 소개되었으므로 올바른 사용의 문제입니다. github.com/ruby/psych/commit/…
Rafe

0

가장 편리한 방법은 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]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.