मैं स्ट्रिंग ऑब्जेक्ट को हैश ऑब्जेक्ट में कैसे परिवर्तित करूं?


136

मेरे पास एक स्ट्रिंग है जो हैश की तरह दिखता है:

"{ :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' } }

स्ट्रिंग में घोंसले के शिकार की कोई भी गहराई हो सकती है। इसमें सभी गुण हैं कि कैसे एक वैध हैश को रूबी में टाइप किया गया है।


मुझे लगता है कि ईवैल यहां कुछ करेंगे। पहले मुझे परखने दो। मुझे लगता है कि मैं बहुत जल्दी सवाल पोस्ट किया। :)
वसीम

ओह, हाँ, बस इसे पारित करने के लिए। :)
वसीम

जवाबों:


79

कॉल करके बनाई गई स्ट्रिंग को उस पर Hash#inspectकॉल करके हैश में बदल दिया जा सकता evalहै। हालाँकि, इसके लिए हैश में सभी वस्तुओं का सही होना आवश्यक है।

यदि मैं हैश से शुरू करता हूं {:a => Object.new}, तो उसका स्ट्रिंग प्रतिनिधित्व है "{:a=>#<Object:0x7f66b65cf4d0>}", और मैं evalइसे हैश में वापस करने के लिए उपयोग नहीं कर सकता क्योंकि #<Object:0x7f66b65cf4d0>वैध रूबिन सिंटैक्स नहीं है।

हालाँकि, अगर यह सब हैश में है तो तार, प्रतीक, संख्या, और सरणियाँ, यह काम करना चाहिए, क्योंकि उन में स्ट्रिंग प्रतिनिधित्व हैं जो वैध रूबी सिंटैक्स हैं।


"अगर हैश में सब कुछ तार, प्रतीक और संख्या है,"। यह बहुत कुछ कहता है। इसलिए मैं एक स्ट्रिंग की वैधता की जाँच कर सकता हूँ ताकि evalयह सुनिश्चित कर सके कि उपरोक्त विवरण उस स्ट्रिंग के लिए मान्य है।
वसीम

1
हां, लेकिन ऐसा करने के लिए आपको या तो एक पूर्ण रूबी पार्सर की आवश्यकता होती है, या आपको यह जानना होगा कि स्ट्रिंग पहले स्थान पर कहां से आई है और आपको पता है कि यह केवल तार, प्रतीक और संख्या उत्पन्न कर सकती है। (स्ट्रिंग की सामग्री पर भरोसा करने के बारे में टॉम्स मिकॉस का जवाब भी देखें।)
केन ब्लूम

13
जहां आप इसका इस्तेमाल करते हैं, वहां लापरवाह रहें। evalगलत जगह पर उपयोग करना एक बहुत बड़ा सुरक्षा छेद है। स्ट्रिंग के अंदर कुछ भी, मूल्यांकन किया जाएगा। तो कल्पना कीजिए कि अगर किसी एपीआई में किसी ने इंजेक्शन लगायाrm -fr
Pithikos

153

विभिन्न स्ट्रिंग के लिए, आप इसे खतरनाक 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('=>', ':')

2
इस जवाब को eval का उपयोग करने से बचने के लिए चुना जाना चाहिए।
माइकल_ज़ैंग

4
तुम भी nils की जगह चाहिए, feJSON.parse(hash_as_string.gsub("=>", ":").gsub(":nil,", ":null,"))
यो लुडके

136

त्वरित और गंदी विधि होगी

eval("{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }") 

लेकिन इसके गंभीर सुरक्षा निहितार्थ हैं।
यह जो कुछ भी पारित किया जाता है उसे निष्पादित करता है, आपको 110% निश्चित होना चाहिए (जैसे कि, रास्ते में कहीं भी कम से कम कोई उपयोगकर्ता इनपुट नहीं), इसमें केवल ठीक से गठित हैश या अप्रत्याशित कीड़े / बाहरी स्थान से भयानक जीव शामिल होंगे जो पॉपिंग शुरू कर सकते हैं।


16
मेरे साथ एक प्रकाश कृपाण है। मैं उन जीवों और बगों की देखभाल कर सकता हूं। :)
वसीम

12
मेरे शिक्षक के अनुसार, EVAL का उपयोग खतरनाक हो सकता है। Eval कोई भी रूबी कोड लेता है और उसे चलाता है। यहां खतरा SQL इंजेक्शन खतरे के अनुरूप है। Gsub बेहतर है।
बोल्डर_रूबी

9
उदाहरण स्ट्रिंग दिखा रहा है कि डेविड का शिक्षक सही क्यों है: '{: आश्चर्य => "# {प्रणाली \" rm -rf * \ "}"} "
ए। विल्सन

13
मैं यहाँ पर्याप्त उपयोग करने के खतरे पर जोर नहीं दे सकता! यह पूरी तरह से निषिद्ध है अगर उपयोगकर्ता इनपुट कभी भी आपके स्ट्रिंग में अपना रास्ता बना सकता है।
डेव कॉलिन्स

अगर आपको लगता है कि आप इसे कभी भी सार्वजनिक रूप से नहीं खोलेंगे, तो कोई और व्यक्ति कर सकता है। हम सभी को पता होना चाहिए कि कैसे कोड का उपयोग उन तरीकों से किया जाता है जिनकी आपने अपेक्षा नहीं की थी। यह एक उच्च शेल्फ पर बेहद भारी चीजों को डालने जैसा है, जिससे यह शीर्ष भारी हो जाता है। आपको कभी भी इस खतरे का रूप नहीं बनाना चाहिए।
स्टीव साइडर

24

शायद YAML.load?


(लोड विधि तार का समर्थन करती है)
मौन

5
इसके लिए पूरी तरह से अलग स्ट्रिंग प्रतिनिधित्व की आवश्यकता होती है, लेकिन यह बहुत ज्यादा सुरक्षित है। (और स्ट्रिंग प्रतिनिधित्व बस के रूप में उत्पन्न करने के लिए आसान है - बस #to_yaml को कॉल करें, बजाय #inspect)
केन ब्लूम

वाह। मुझे नहीं पता था कि तार w / yaml को पार्स करना इतना आसान था। यह मेरी लाइन लाइन बैश कमांड की श्रृंखला लेता है जो डेटा उत्पन्न करता है और समझदारी से इसे रूबी हैश डब्ल्यू / ओ में किसी भी स्ट्रिंग प्रारूप की मालिश में बदल देता है।
भूलभुलैया

यह__मैं मेरी समस्या को हल करता है क्योंकि मेरे पास स्ट्रिंग उत्पन्न होने के तरीके का कुछ नियंत्रण है। धन्यवाद!
mlabarca

23

यह छोटा सा स्निपेट करेगा, लेकिन मैं इसे नेस्टेड हैश के साथ काम करते हुए नहीं देख सकता। मुझे लगता है कि यह बहुत प्यारा है

STRING.gsub(/[{}:]/,'').split(', ').map{|h| h1,h2 = h.split('=>'); {h1 => h2}}.reduce(:merge)

चरण 1. मैं '{', '}' और ':' को समाप्त करता हूं। 2. मैं स्ट्रिंग पर विभाजित करता हूं जहां भी यह पाता है ',' 3. मैं विभाजन के साथ बनाए गए प्रत्येक सब्सट्रिंग को विभाजित करता हूं, जब भी वह पाता है a = => '। फिर, मैं हैश के दो किनारों के साथ एक हैश बनाता हूं मैं बस अलग हो जाता हूं। 4. मैं हैश की एक सरणी के साथ बचा हुआ हूं जिसे मैं फिर एक साथ मिलाता हूं।

उदाहरण INPUT: "{: user_id => 11,: blog_id => 2,: टिप्पणी_id => 1}" परिणाम पर जाएं: {"user_id" => "11", "blog_id ="> "2", "comment_id" = > "1"}


1
कि एक बीमार oneliner है! :) +1
blushrt

3
क्या यह भी कड़े हैश के अंदर के मूल्यों{}: से पात्रों को दूर नहीं करेगा ?
व्लादिमीर पैंटेलेव

@VladimirPanteleev तुम सही हो, यह होगा। अच्छा पकड़ा! आप किसी भी दिन मेरी कोड समीक्षा कर सकते हैं :)
hrdwdmrbl

20

अब तक के समाधान कुछ मामलों को कवर करते हैं लेकिन कुछ को याद करते हैं (नीचे देखें)। यहाँ एक अधिक गहन (सुरक्षित) रूपांतरण पर मेरा प्रयास है। मैं एक कोने के मामले को जानता हूं जो इस समाधान को संभालता नहीं है जो एकल चरित्र प्रतीकों से बना है, लेकिन पात्रों की अनुमति है। उदाहरण के लिए{:> => :<} एक वैध माणिक हैश है।

मैंने इस कोड को जीथब पर भी डाल दिया । यह कोड सभी रूपांतरणों का अभ्यास करने के लिए एक परीक्षण स्ट्रिंग के साथ शुरू होता है

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)

यहाँ अन्य समाधानों पर कुछ नोट दिए गए हैं


बहुत अच्छा समाधान। आप उस विशेष अजीबता को संभालने के लिए सभी :nilका gsub जोड़ सकते हैं :null
स्टीवटूरकिन

1
इस समाधान में बहु-स्तरीय हैश पर पुनरावर्ती रूप से काम करने का बोनस भी है, क्योंकि यह JSON # पार्स का लाभ उठाता है। मुझे अन्य समाधानों पर घोंसले के शिकार से कुछ परेशानी थी।
पैट्रिक

17

मुझे भी यही समस्या थी। मैं रेडिस में एक हैश का भंडारण कर रहा था। उस हैश को पुनः प्राप्त करते समय, यह एक स्ट्रिंग था। मैं eval(str)सुरक्षा चिंताओं के कारण फोन नहीं करना चाहता था । मेरा समाधान रूबी हैश स्ट्रिंग के बजाय हैश को जौन स्ट्रिंग के रूप में सहेजना था। यदि आपके पास विकल्प है, तो json का उपयोग करना आसान है।

  redis.set(key, ruby_hash.to_json)
  JSON.parse(redis.get(key))

टीएल; डीआर: उपयोग to_jsonऔरJSON.parse


1
यह अब तक का सबसे अच्छा जवाब है। to_jsonऔरJSON.parse
ardochhigh

3
जिसने भी मुझे नीचा दिखाया। क्यों? मेरे पास एक ही मुद्दा था, रूबी हैश के एक स्ट्रिंग प्रतिनिधित्व को वास्तविक हैश ऑब्जेक्ट में बदलने की कोशिश करना। मुझे एहसास हुआ कि मैं गलत समस्या को हल करने की कोशिश कर रहा था। मैंने महसूस किया कि यहाँ पूछे गए प्रश्न को हल करना त्रुटि प्रवण और असुरक्षित था। मुझे एहसास हुआ कि मुझे अपने डेटा को अलग-अलग स्टोर करने और एक प्रारूप का उपयोग करने की आवश्यकता है जो कि वस्तुओं को सुरक्षित रूप से अनुक्रमित और डिस्अरीलाइज़ करने के लिए डिज़ाइन किया गया है। टीएल; डीआर: मेरे पास ओपी के रूप में एक ही सवाल था, और एहसास हुआ कि जवाब एक अलग सवाल पूछना था। इसके अलावा, यदि आप मुझे वोट देते हैं, तो कृपया प्रतिक्रिया दें ताकि हम सभी एक साथ सीख सकें।
जेरेड मेनार्ड

3
एक व्याख्यात्मक टिप्पणी के बिना डाउनवोटिंग स्टैक ओवरफ्लो का कैंसर है।
ardochhigh

1
हां डाउनवोटिंग में एक स्पष्टीकरण की आवश्यकता होती है और यह दर्शाती है कि डाउनवोट कौन है।
निक रेस

2
ओपी के प्रश्न पर इस उत्तर को और अधिक लागू करने के लिए, यदि किसी हैश के आपके स्ट्रिंग प्रतिनिधित्व को 'स्ट्रैंगआउट' कहा जाता है, तो आपको हैश = JSON.parse (strungout.to_json) करने में सक्षम होना चाहिए और फिर हैश के माध्यम से हैश के अंदर अपनी वस्तुओं का चयन करें [ 'कीनेम'] सामान्य रूप से।
cixelsyd

11

मैं ActiveSupport :: JSON का दुरुपयोग करना पसंद करता हूं। उनका दृष्टिकोण हैश को यमल में बदलना और फिर उसे लोड करना है। दुर्भाग्य से याम्ल के लिए रूपांतरण सरल नहीं है और यदि आप पहले से ही अपनी परियोजना में एएस नहीं हैं, तो आप इसे एएस से उधार लेना चाहेंगे।

हमें किसी भी प्रतीकों को नियमित स्ट्रिंग-कुंजियों में बदलना होगा क्योंकि प्रतीक JSON में उपयुक्त नहीं हैं।

हालांकि, इसकी हैश को संभालने में असमर्थ है कि उनमें एक तारीख स्ट्रिंग है (हमारी तारीख के तार अंत में तार से घिरे नहीं हैं, जहां बड़ा मुद्दा आता है):

string = '{' last_request_at ': 2011-12-28 23:00:00 UTC}' ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>', ' : '))

जब यह दिनांक मान को पार्स करने का प्रयास करता है तो अमान्य JSON स्ट्रिंग त्रुटि का परिणाम होगा।

इस मामले को कैसे संभालना है, इस पर कोई सुझाव पसंद आएगा


2
सूचक के लिए धन्यवाद .d timecode, यह मेरे लिए बहुत अच्छा काम किया। मुझे इसका परीक्षण करने के लिए JSON प्रतिक्रिया बदलने की आवश्यकता थी। यहाँ कोड का मैंने उपयोग किया है:ActiveSupport::JSON.decode(response.body, symbolize_keys: true)
एंड्रयू फिलिप्स

9

4.1 रेल में काम करता है और उद्धरण के बिना प्रतीकों का समर्थन करता है {: a => 'b'}

इसे इनिशियलाइज़र फ़ोल्डर में जोड़ें:

class String
  def to_hash_object
    JSON.parse(self.gsub(/:([a-zA-z]+)/,'"\\1"').gsub('=>', ': ')).symbolize_keys
  end
end

कमांड लाइन पर काम करता है, लेकिन मुझे "स्टैक लेवल टू डीप" मिलता है, जब मैंने इसे एक इंटेस्टाइज़र में डाला ...
एलेक्स एडेलस्टीन

2

मैंने एक मणि हैश_पर बनाया है जो पहले जांच करता है कि कोई हैश सुरक्षित है या ruby_parserमणि का उपयोग नहीं कर रहा है । तभी, यह लागू होता है 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)

Https://github.com/bibstha/ruby_hash_parser/blob/master/test/test_hash_parser.rb में परीक्षण आपको उन चीजों के और अधिक उदाहरण देते हैं जिन्हें मैंने सुनिश्चित करने के लिए परीक्षण किया है कि सुरक्षित सुरक्षित है।


2

कृपया इस समाधान पर विचार करें। लाइब्रेरी + कल्पना:

फ़ाइल 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

फ़ाइल 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

1
it "generally works" लेकिन जरूरी नहीं? मैं उन परीक्षणों में अधिक क्रियाशील रहूंगा। it "converts strings to object" { expect('...').to eql ... } it "supports nested objects" { expect('...').to eql ... }
लेक्स

अरे @Lex, क्या विधि अपने RubyDoc टिप्पणी में वर्णित है। परीक्षण बेहतर यह फिर से राज्य नहीं है, यह निष्क्रिय पाठ के रूप में अनावश्यक विवरण पैदा करेगा। इस प्रकार, "सामान्‍यत: काम करता है" सामान को स्‍पष्‍ट करने का एक अच्‍छा सूत्र है, ठीक है, आम तौर पर काम करता है। चीयर्स!
एलेक्स फोर्टुना

हाँ, दिन के अंत में जो भी काम करता है। कोई भी परीक्षण बिना किसी परीक्षण से बेहतर है। व्यक्तिगत रूप से मैं स्पष्ट विवरणों का प्रशंसक हूं, लेकिन यह सिर्फ एक प्राथमिकता है।
लेक्स

1

मैं इस उद्देश्य के लिए एक-लाइनर लिखने के बाद इस सवाल पर आया था, इसलिए मैं किसी को मदद करने के मामले में अपना कोड साझा करता हूं। एक स्ट्रिंग के लिए केवल एक स्तर की गहराई और संभव खाली मान (लेकिन शून्य नहीं) के साथ काम करता है, जैसे:

"{ :key_a => 'value_a', :key_b => 'value_b', :key_c => '' }"

कोड है:

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]}

0

एक ऐसे ही मुद्दे पर भाग गया, जिसे निष्कासन () का उपयोग करने की आवश्यकता थी।

मेरी स्थिति, मैं एक एपीआई से कुछ डेटा खींच रहा था और इसे स्थानीय रूप से फ़ाइल में लिख रहा था। फिर फ़ाइल से डेटा खींचने और हैश का उपयोग करने में सक्षम होने के नाते।

मैंने फ़ाइल की सामग्री को एक चर में पढ़ने के लिए 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 का उपयोग कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.