क्या सबसे अच्छा तरीका है कि एक जोंस स्वरूपित कुंजी मूल्य जोड़ी को रूबी हैश को कुंजी के रूप में प्रतीक के साथ परिवर्तित करना है?


104

मैं सोच रहा हूं कि प्रतीक के रूप में रूबी हैश को 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

जवाबों:


256

json string का उपयोग करते समय json string को पार्स करते समय आप 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
रूबी 1.9 में यह पुस्तकालय भी शामिल है।
साइमन पेरपेलित्सा

क्या यह नहीं हुआ करता था :symbolize_keys? वह नाम क्यों बदल गया?
लुकास

5
@ लुकास: symbolize_keysएक रेल की बात है।
wyattisimo


19

लेवेंटिक्स, आपके उत्तर के लिए धन्यवाद।

Marshal.load (Marshal.dump (ज)) , क्योंकि यह मूल कुंजी प्रकार को बरकरार रखता है विधि शायद विभिन्न तरीकों से ज्यादातर अखंडता है रिकर्सिवली

यह महत्वपूर्ण है कि आपके पास स्ट्रिंग और प्रतीक कुंजियों के मिश्रण के साथ नेस्टेड हैश है और आप डिकोड पर मिश्रण को संरक्षित करना चाहते हैं (उदाहरण के लिए, यह तब हो सकता है जब आपके हैश में अत्यधिक जटिल / नेस्टेड तीसरे के अलावा आपके स्वयं के कस्टम ऑब्जेक्ट शामिल हों -पार्टी ऑब्जेक्ट्स जिनकी कुंजियाँ आप किसी भी कारण से जोड़ / तब्दील नहीं कर सकते हैं, जैसे प्रोजेक्ट टाइम की कमी)।

उदाहरण के लिए:

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.dump(h) )
  => { :youtube => { :search => "daffy", "history" => ["goofy", "mickey"] } }

जब तक कोई कमी नहीं है कि मैं अनजान हूँ, मुझे लगता है कि विधि 3 जाने का रास्ता है।

चियर्स


2
यहाँ कोई गारंटी नहीं है कि आपके पास दूसरी तरफ का नियंत्रण है, इसलिए मेरा मानना ​​है कि आपको JSON प्रारूपण से चिपके रहना होगा। यदि आपके पास दोनों पक्षों का पूर्ण नियंत्रण है, तो मार्शल वास्तव में एक अच्छा प्रारूप है, लेकिन यह सामान्य प्रयोजन के क्रम के लिए उपयुक्त नहीं है।
Chills42

5

ट्रिक को करने के लिए कुछ भी नहीं बनाया गया है, लेकिन JSON रत्न का उपयोग करके कोड लिखने के लिए इसे लिखना बहुत कठिन नहीं है। symbolize_keysयदि आप उस का उपयोग कर रहे हैं, तो रेल में निर्मित एक विधि है, लेकिन यह आपकी ज़रूरत के अनुसार प्रमुखता से प्रतीकों का प्रतीक नहीं है।

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

जैसा कि लेवेंटिक्स ने कहा, JSON मणि केवल दोहरे उद्धृत तारों को संभालती है (जो तकनीकी रूप से सही है - 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 मान्य होगा (जैसे कि आप इसे स्वयं बना रहे हैं!)
edavey

यह काम नहीं करता है। JSON.parse(JSON.generate([:a])) # => ["a"]
जस्टिन एल।

2
ऐसा इसलिए है क्योंकि JSON प्रतीकों का प्रतिनिधित्व नहीं कर सकता है। आप उपयोग कर सकते हैं: Marshal.load(Marshal.dump([:a]))इसके बजाय।
लेवेंटिक्स

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

@ क्या आपका मतलब है कि 2013 का यह सुरक्षा छेद आज भी तय नहीं है?
बर्ट ब्रुइनोगे

1
रूबी 2.2 के बाद से सिंबल GC'ed हैं। YAML.loadका अर्थ है मनमानी वस्तुओं को क्रमबद्ध करना (जैसे कैश के लिए)। प्रस्तावित YAML.safe_loadउस ब्लॉग पोस्ट के कुछ महीनों बाद पेश किया गया है, इसलिए यह सही चीज़ का उपयोग करने की बात है: github.com/ruby/psych/commit/…
Rafe

0

सबसे अच्छा तरीका है good_hash रत्न का उपयोग करके: 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]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.