Ruby: How to turn a hash into HTTP parameters?

2019-01-16 00:06发布

That is pretty easy with a plain hash like

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

which would translate into

"a=a&b=b"

But what do you do with something more complex like

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

which should translate into

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

Or even worse, (what to do) with something like:

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

Thanks for the much appreciated help with that!

13条回答
Viruses.
2楼-- · 2019-01-16 00:47

Steal from Merb:

# 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

See http://noobkit.com/show/ruby/gems/development/merb/hash/to_params.html

查看更多
Melony?
3楼-- · 2019-01-16 00:48

If you are using Ruby 1.9.2 or later, you can use URI.encode_www_form if you don't need arrays.

E.g. (from the Ruby docs in 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"

You'll notice that array values are not set with key names containing [] like we've all become used to in query strings. The spec that encode_www_form uses is in accordance with the HTML5 definition of application/x-www-form-urlencoded data.

查看更多
别忘想泡老子
4楼-- · 2019-01-16 00:48

There's a much easier way to do this if you're using Rails: http://apidock.com/rails/ActiveSupport/CoreExtensions/Hash/to_query

So you could do:

{:a => "a", :b => "b"}.to_query
查看更多
走好不送
5楼-- · 2019-01-16 00:51

I know this is an old question, but I just wanted to post this bit of code as I could not find a simple gem to do just this task for me.

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

Rolled up as gem here: https://github.com/simen/queryparams

查看更多
放我归山
6楼-- · 2019-01-16 00:52

For basic, non-nested hashes, Rails/ActiveSupport has 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

查看更多
Juvenile、少年°
7楼-- · 2019-01-16 00:52

The best approach it is to use Hash.to_params which is the one working fine with arrays.

{a: 1, b: [1,2,3]}.to_param
"a=1&b[]=1&b[]=2&b[]=3"
查看更多
登录 后发表回答