Ruby:如何将哈希转换为HTTP参数?

Jul*_*oux 198 ruby parameters http hashmap

用简单的哈希就很容易了

{:a => "a", :b => "b"} 
Run Code Online (Sandbox Code Playgroud)

这将转化为

"a=a&b=b"
Run Code Online (Sandbox Code Playgroud)

但你如何处理更复杂的事情呢?

{:a => "a", :b => ["c", "d", "e"]} 
Run Code Online (Sandbox Code Playgroud)

这应该转化为

"a=a&b[0]=c&b[1]=d&b[2]=e" 
Run Code Online (Sandbox Code Playgroud)

或者更糟糕的是,(做什么)有类似的事情:

{:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]
Run Code Online (Sandbox Code Playgroud)

感谢非常感谢的帮助!

Gab*_*esy 260

对于基本的非嵌套哈希,Rails/ActiveSupport具有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"
Run Code Online (Sandbox Code Playgroud)

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

  • 没有 Rails:需要`require 'active_support/all'` (3认同)

Bo *_*nes 145

如果您使用的是Ruby 1.9.2或更高版本,URI.encode_www_form则可以在不需要数组时使用.

例如(来自1.9.3中的Ruby文档):

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"
Run Code Online (Sandbox Code Playgroud)

您会注意到,数组值未使用包含[]我们在查询字符串中习惯使用的键名来设置.使用的规范encode_www_form符合HTML5 application/x-www-form-urlencoded数据定义.

  • +1,这是迄今为止最好的.它不依赖于Ruby本身以外的任何来源. (7认同)
  • 请注意,这对数组值的结果与“Addressable::URI”和 ActiveSupport 的“Object#to_query”不同。 (3认同)

Bob*_*man 86

更新:此功能已从gem中删除.

朱利安,你的自我回答是一个很好的答案,我无耻地借用它,但它没有正确地逃脱保留字符,还有一些其他边缘情况,它崩溃了.

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]"
Run Code Online (Sandbox Code Playgroud)

宝石是' 可寻址的 '

gem install addressable
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,不幸的是,从2.3(https://github.com/sporkmonger/addressable/commit/f51e290b5f68a98293327a7da84eb9e2d5f21c62)开始,此行为已从Addressable中删除 (3认同)
  • 它不处理布尔值,这显然是不可取的:{"a"=>"a&b = b"}.to_params (2认同)
  • @oif_vet您能说出已删除的行为吗?Bob建议使用可寻址宝石来解决原始海报问题的方法对我来说是可寻址的 - 2.3.2. (2认同)

iai*_*ain 55

无需加载膨胀的ActiveSupport或滚动自己的,你可以使用Rack::Utils.build_queryRack::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"
Run Code Online (Sandbox Code Playgroud)

它甚至处理数组:

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"]}
Run Code Online (Sandbox Code Playgroud)

或者更难嵌套的东西:

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"}]}
Run Code Online (Sandbox Code Playgroud)

  • @davidgoli Erm,不在***Rack***它不是https://github.com/rack/rack/blob/1.5.2/lib/rack/utils.rb#L140.如果你想在Rails中使用它,肯定它就像`require'bag'`一样简单?考虑到所有主要的Ruby Web框架都建立在Rack之上,它必须存在. (8认同)
  • @EdRuder没有_properly_因为没有公认的标准.它所展示的是,它与其他任何人的尝试相比都更接近,从其他答案来看. (3认同)

Avd*_*vdi 9

从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
Run Code Online (Sandbox Code Playgroud)

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

  • 而且没有任何逃避之王. (2认同)

Hub*_*bro 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"
Run Code Online (Sandbox Code Playgroud)


Jul*_*oux 5

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
Run Code Online (Sandbox Code Playgroud)