रूबी: एक हैश को HTTP मापदंडों में कैसे बदलना है?


205

यह एक सादे हैश की तरह बहुत आसान है

{:a => "a", :b => "b"} 

जिसका अनुवाद होगा

"a=a&b=b"

लेकिन आप कुछ और जटिल जैसे क्या करते हैं

{:a => "a", :b => ["c", "d", "e"]} 

जिसका अनुवाद करना चाहिए

"a=a&b[0]=c&b[1]=d&b[2]=e" 

या इससे भी बदतर, (क्या करें) कुछ के साथ:

{:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]

उस के साथ बहुत सराहना की मदद के लिए धन्यवाद!


ऐसा लगता है कि आप JSON को HTTP पैरामेट्स में बदलना चाहते हैं ... शायद आपको एक अलग एन्कोडिंग की आवश्यकता है?
CookieOfFortune

हम, यह वास्तव में Json नहीं है, लेकिन एक रूबी हैश ... यकीन नहीं है कि मैं समझता हूं कि यहाँ एन्कोडिंग क्यों मायने रखती है।
जुलिएन जेनस्टौक्स

उत्तरदाताओं द्वारा प्रचार किया जाना चाहिए। यहां बहुत सारे शानदार रोल-ऑफ-ही जवाब हैं (कई उच्च स्कोर के साथ), लेकिन ActiveSupport ने इसके लिए मानकीकृत समर्थन जोड़ा है, जिससे वार्तालाप मूट का प्रतिपादन होता है। दुर्भाग्य से, लेमनर का जवाब अभी भी सूची में दफन है।
नोआच म्रेडमैन

2
@ मेरी राय में, कोई भी जवाब जो किसी पुस्तकालय पर भरोसा करने के लिए कहता है कि भारी बंदर पैच कोर कक्षाएं दफन रहनी चाहिए। उन पैच की एक बड़ी संख्या के लिए औचित्य सबसे अच्छा है ( इस लेख में येहुदा काट्ज़ की टिप्पणियों पर एक नज़र डालें ), यह एक उत्कृष्ट उदाहरण है। YMMV, लेकिन मेरे लिए, एक वर्ग विधि के साथ कुछ या जो ऑब्जेक्ट और हैश को नहीं खोलता है, और जहां लेखक नहीं कहेंगे "बस हमारे साथ संघर्ष न करें!" ज्यादा बेहतर होगा।
इयान

जवाबों:


86

अद्यतन: यह कार्यक्षमता मणि से हटा दी गई थी।

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

require "addressable/uri"
uri = Addressable::URI.new
uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
uri.query
# => "a=a&b[0]=c&b[1]=d&b[2]=e"
uri.query_values = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}
uri.query
# => "a=a&b[0][c]=c&b[0][d]=d&b[1][e]=e&b[1][f]=f"
uri.query_values = {:a => "a", :b => {:c => "c", :d => "d"}}
uri.query
# => "a=a&b[c]=c&b[d]=d"
uri.query_values = {:a => "a", :b => {:c => "c", :d => true}}
uri.query
# => "a=a&b[c]=c&b[d]"
uri.query_values = {:a => "a", :b => {:c => "c", :d => true}, :e => []}
uri.query
# => "a=a&b[c]=c&b[d]"

मणि ' पता करने योग्य ' है

gem install addressable

1
धन्यवाद! किनारे के मामले क्या हैं जहां मेरा समाधान टूट जाता है? इसलिए मैं इसे ऐनक में जोड़ सकता हूं?
जुलिएन जेनस्टौक्स

2
यह बूलियन्स को संभालता नहीं है, और यह स्पष्ट रूप से अवांछनीय है: {"a" => "a & b = b"}। to_params
Bob Aman

3
FYI करें, दुर्भाग्य से इस व्यवहार को 2.3 से हटा दिया गया है (2.3 github.com/sporkmonger/addressable/commit/… ) के रूप में
23

2
@oif_vet क्या आप कह सकते हैं कि व्यवहार को हटा दिया गया है? बॉब ने मूल पोस्टर की समस्या को हल करने के लिए पता योग्य मणि का उपयोग करने का सुझाव दिया, जो मेरे लिए पता-2.3.2 के रूप में काम करता है।
शेल्डन

1
@sheldonh, नहीं, @oif_vet सही है। मैंने इस व्यवहार को हटा दिया। query_valuesम्यूटेटर के इनपुट के रूप में डीप नेस्टेड स्ट्रक्चर अब एड्रेसेबल में समर्थित नहीं हैं ।
बॉब अमन

269

बेसिक, नॉन-नेस्टेड हैश के लिए, रेल्स / एक्टिवसुपोर्ट है Object#to_query

>> {:a => "a", :b => ["c", "d", "e"]}.to_query
=> "a=a&b%5B%5D=c&b%5B%5D=d&b%5B%5D=e"
>> CGI.unescape({:a => "a", :b => ["c", "d", "e"]}.to_query)
=> "a=a&b[]=c&b[]=d&b[]=e"

http://api.rubyonrails.org/classes/Object.html#method-i-to_query


1
आप क्यों कहते हैं कि यह टूट गया है? आपके द्वारा दिखाया गया उत्पादन ठीक है, है ना?
टोकन

मैंने बस कोशिश की और आप सही प्रतीत हो रहे हैं। हो सकता है कि मेरा बयान मूल रूप से क्वेरी स्ट्रिंग को पूर्ववर्ती संस्करण के तरीके के कारण था (मैं इसे पिछले 'बी' मानों को अधिलेखित करने के लिए याद करता था)। 2011-03-10 11:19:40 11:40:19 पर साइटकेन्ट्रोलर # इंडेक्स के रूप में प्रसंस्करण HTML पैरामीटर: {"a" => "a", "b" => ["c", "d", "e"]}
Gabe Martin-Dempesy

नेस्टेड हैश होने पर क्या गलत है? नेस्टेड हैश होने पर मैं इसका उपयोग क्यों नहीं कर सकता? मेरे लिए, यह केवल नेस्टेड हैश से बच जाता है, http अनुरोध में इसका उपयोग करने में कोई समस्या नहीं होनी चाहिए।
सैम

2
रेल के बिना: require 'active_support/all'की जरूरत है
डोरियन

कम से कम रेल 5.2 के साथ to_queryशून्य मान को ठीक से नहीं संभालता है। { a: nil, b: '1'}.to_query == "a=&b=1", लेकिन रैक और सीजीआई दोनों a=एक खाली स्ट्रिंग के रूप में हैं, नहीं nil। मुझे अन्य सर्वरों के समर्थन के बारे में निश्चित नहीं है, लेकिन रेल के साथ, सही क्वेरी स्ट्रिंग होना चाहिए a&b=1। मुझे लगता है कि यह गलत है कि रेल एक क्वेरी स्ट्रिंग का उत्पादन नहीं कर सकती है जो सही ढंग से खुद को पार्स कर रही है ...
jsmartt

153

यदि आप रूबी 1.9.2 या इसके बाद के संस्करण का उपयोग कर रहे हैं, URI.encode_www_formतो आप एरेज़ की आवश्यकता नहीं होने पर उपयोग कर सकते हैं ।

जैसे (रूबी डॉक्स से 1.9.3 में):

URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
#=> "q=ruby&lang=en"
URI.encode_www_form("q" => "ruby", "lang" => "en")
#=> "q=ruby&lang=en"
URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
#=> "q=ruby&q=perl&lang=en"
URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
#=> "q=ruby&q=perl&lang=en"

आप देखेंगे कि सरणी मान ऐसे प्रमुख नामों के साथ सेट नहीं किए []गए हैं जैसे कि हम सभी क्वेरी स्ट्रिंग्स में उपयोग किए जाते हैं। encode_www_formउपयोग की जाने वाली युक्ति HTML5 application/x-www-form-urlencodedडेटा की परिभाषा के अनुसार है ।


8
+1, यह अब तक का सबसे अच्छा है। यह रूबी के बाहर किसी भी स्रोत पर निर्भर नहीं करता है।
डेनियल

+1 '{: a => "a": b => {: c => "c",: d => true}, e => []] उदाहरण के साथ ठीक काम करता है
ड्यूक

1
रूबी 2.0 के साथ काम करने के लिए प्रतीत नहीं होता है - हैश का {:c => "c", :d => true}निरीक्षण किया गया प्रतीत होता है, इसलिए इसे स्ट्रिंग के रूप में भेजा जाता है।
user208769

1
यह ऊपर बड़े स्निपेट का एक भाग था -ruby -ruri -e 'puts RUBY_VERSION; puts URI.encode_www_form({:a => "a", :b => {:c => "c", :d => true}, :e => []})' # outputs 2.0.0 a=a&b=%7B%3Ac%3D%3E%22c%22%2C+%3Ad%3D%3Etrue%7D&
user208769

1
ध्यान दें कि यह दोनों Addressable::URIऔर ActiveSupport की तुलना में सरणी मानों के लिए अलग-अलग परिणाम है Object#to_query
मैट हगिंस

61

फूला हुआ ActiveSupport लोड करने या अपना स्वयं का रोल करने की आवश्यकता नहीं है, आप उपयोग कर सकते हैं Rack::Utils.build_queryऔर Rack::Utils.build_nested_queryयहाँ एक ब्लॉग पोस्ट है जो एक अच्छा उदाहरण देता है:

require 'rack'

Rack::Utils.build_query(
  authorization_token: "foo",
  access_level: "moderator",
  previous: "index"
)

# => "authorization_token=foo&access_level=moderator&previous=index"

यह भी सरणियों को संभालता है:

Rack::Utils.build_query( {:a => "a", :b => ["c", "d", "e"]} )
# => "a=a&b=c&b=d&b=e"
Rack::Utils.parse_query _
# => {"a"=>"a", "b"=>["c", "d", "e"]}

या अधिक कठिन नेस्टेड सामान:

Rack::Utils.build_nested_query( {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}] } )
# => "a=a&b[][c]=c&b[][d]=d&b[][e]=e&b[][f]=f"
Rack::Utils.parse_nested_query _
# => {"a"=>"a", "b"=>[{"c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f"}]}

आपका नेस्टेड उदाहरण दर्शाता है कि यह ठीक से काम नहीं करता है - जब आप शुरू करते हैं, :bतो दो हैश की एक सरणी होती है। आप :bएक बड़े हैश की एक सरणी होने के साथ समाप्त होते हैं ।
एड रुडर

3
@EdRuder वहाँ कोई ठीक से नहीं है क्योंकि कोई मानक नहीं है। यह क्या दिखाता है कि यह किसी और के प्रयास की तुलना में बहुत करीब है, अन्य उत्तरों को देखते हुए।
0

1
इस पद्धति को रेल 2.3.8 से हटा दिया गया है: apidock.com/rails/Rack/Utils/build_query
davidgoli

8
@davidgoli Erm, रैक में नहीं यह github.com/rack/rack/blob/1.5.2/lib/rack/utils.rb#L140 नहीं है । यदि आप इसे रेल में उपयोग करना चाहते हैं, तो निश्चित रूप से यह उतना ही सरल है require 'rack'? यह वहाँ होना चाहिए, यह देखते हुए कि सभी प्रमुख रूबी वेब फ्रेम अब रैक के शीर्ष पर बनाए गए हैं।
Iain

@EdRuder ActiveSupport की to_query2 सरणियों (v4.2) को भी मिलाता है ।
केल्विन

9

मर्ब से चोरी:

# File merb/core_ext/hash.rb, line 87
def to_params
  params = ''
  stack = []

  each do |k, v|
    if v.is_a?(Hash)
      stack << [k,v]
    else
      params << "#{k}=#{v}&"
    end
  end

  stack.each do |parent, hash|
    hash.each do |k, v|
      if v.is_a?(Hash)
        stack << ["#{parent}[#{k}]", v]
      else
        params << "#{parent}[#{k}]=#{v}&"
      end
    end
  end

  params.chop! # trailing &
  params
end

Http://noobkit.com/show/ruby/gems/development/merb/hash/to_params.html देखें


1
दुर्भाग्य से, यह काम नहीं करता है जब हमारे पास
परमों के

2
और भागने का कोई राजा नहीं करता।
अर्नेस्ट

9

यदि आपको केवल सरल ASCII कुंजी / मान क्वेरी स्ट्रिंग्स का समर्थन करने की आवश्यकता है, तो यह एक छोटा और मीठा एक लाइनर है:

hash = {"foo" => "bar", "fooz" => 123}
# => {"foo"=>"bar", "fooz"=>123}
query_string = hash.to_a.map { |x| "#{x[0]}=#{x[1]}" }.join("&")
# => "foo=bar&fooz=123"

4
class Hash
  def to_params
    params = ''
    stack = []

    each do |k, v|
      if v.is_a?(Hash)
        stack << [k,v]
      elsif v.is_a?(Array)
        stack << [k,Hash.from_array(v)]
      else
        params << "#{k}=#{v}&"
      end
    end

    stack.each do |parent, hash|
      hash.each do |k, v|
        if v.is_a?(Hash)
          stack << ["#{parent}[#{k}]", v]
        else
          params << "#{parent}[#{k}]=#{v}&"
        end
      end
    end

    params.chop! 
    params
  end

  def self.from_array(array = [])
    h = Hash.new
    array.size.times do |t|
      h[t] = array[t]
    end
    h
  end

end

3
{:a=>"a", :b=>"b", :c=>"c"}.map{ |x,v| "#{x}=#{v}" }.reduce{|x,v| "#{x}&#{v}" }

"a=a&b=b&c=c"

यहाँ एक और तरीका है। सरल प्रश्नों के लिए।


2
आप वास्तव में यह सुनिश्चित करना चाहते हैं कि आप ठीक से अपनी कुंजी और मूल्यों से बच रहे हैं। साधारण मामलों के लिए भी। यह तुम्हें काटेगा।
ज्रोचाइंड

2

मुझे पता है कि यह एक पुराना सवाल है, लेकिन मैं सिर्फ इस कोड को पोस्ट करना चाहता था क्योंकि मुझे यह काम करने के लिए एक साधारण रत्न नहीं मिला।

module QueryParams

  def self.encode(value, key = nil)
    case value
    when Hash  then value.map { |k,v| encode(v, append_key(key,k)) }.join('&')
    when Array then value.map { |v| encode(v, "#{key}[]") }.join('&')
    when nil   then ''
    else            
      "#{key}=#{CGI.escape(value.to_s)}" 
    end
  end

  private

  def self.append_key(root_key, key)
    root_key.nil? ? key : "#{root_key}[#{key.to_s}]"
  end
end

रत्न के रूप में यहां लुढ़का: https://github.com/simen/queryparams


1
URI.escape != CGI.escapeऔर URL के लिए आप पहले वाला चाहते हैं।
अर्नेस्ट

2
वास्तव में, @Ernest नहीं। जब उदाहरण के लिए आपके url के पैरामीटर के रूप में किसी अन्य url को एम्बेड किया जाता है (यह कहता है कि यह लॉगिन के बाद रीडायरेक्ट होने वाला url है) URI.escape 'रखेगा?' और आसपास के url को तोड़ने में एम्बेडेड url का '&', जबकि CGI.escape सही तरीके से बाद में% 3F और% 26 के रूप में उन्हें हटा देगा। CGI.escape("http://localhost/search?q=banana&limit=7") => "http%3A%2F%2Flocalhost%2Fsearch%3Fq%3Dbanana%26limit%3D7" URI.escape("http://localhost/search?q=banana&limit=7") => "http://localhost/search?q=banana&limit=7"
svale

2

सबसे अच्छा तरीका यह है कि Hash.to_params का उपयोग करना है जो सरणियों के साथ ठीक काम कर रहा है।

{a: 1, b: [1,2,3]}.to_param
"a=1&b[]=1&b[]=2&b[]=3"

रेल के बिना: require 'active_support/all'की जरूरत है
डोरियन

1

यदि आप एक फैराडे अनुरोध के संदर्भ में हैं, तो आप सिर्फ परम हैश को पास कर सकते हैं क्योंकि दूसरा तर्क और फैराडे इसमें से उचित परम URL भाग बनाने का ध्यान रखता है:

faraday_instance.get(url, params_hsh)

0

मुझे इस रत्न का उपयोग करना पसंद है:

https://rubygems.org/gems/php_http_build_query

नमूना उपयोग:

puts PHP.http_build_query({"a"=>"b","c"=>"d","e"=>[{"hello"=>"world","bah"=>"black"},{"hello"=>"world","bah"=>"black"}]})

# a=b&c=d&e%5B0%5D%5Bbah%5D=black&e%5B0%5D%5Bhello%5D=world&e%5B1%5D%5Bbah%5D=black&e%5B1%5D%5Bhello%5D=world

0
require 'uri'

class Hash
  def to_query_hash(key)
    reduce({}) do |h, (k, v)|
      new_key = key.nil? ? k : "#{key}[#{k}]"
      v = Hash[v.each_with_index.to_a.map(&:reverse)] if v.is_a?(Array)
      if v.is_a?(Hash)
        h.merge!(v.to_query_hash(new_key))
      else
        h[new_key] = v
      end
      h
    end
  end

  def to_query(key = nil)
    URI.encode_www_form(to_query_hash(key))
  end
end

2.4.2 :019 > {:a => "a", :b => "b"}.to_query_hash(nil)
 => {:a=>"a", :b=>"b"}

2.4.2 :020 > {:a => "a", :b => "b"}.to_query
 => "a=a&b=b"

2.4.2 :021 > {:a => "a", :b => ["c", "d", "e"]}.to_query_hash(nil)
 => {:a=>"a", "b[0]"=>"c", "b[1]"=>"d", "b[2]"=>"e"}

2.4.2 :022 > {:a => "a", :b => ["c", "d", "e"]}.to_query
 => "a=a&b%5B0%5D=c&b%5B1%5D=d&b%5B2%5D=e"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.