Question

I'm sending a POST request using HTTParty. One of the variables required is an array. This is the code I'm using to send:

response = HTTParty.post url, :body => 
    {"key"=>'XYZ123',
    "content"=>
        [{"placename"=>"placeholder",
        "placecontent"=>"sample content"}],
    etc. }

The API needs to see:

"content": [
    {
        "placename": "placeholder",
        "placecontent": "sample content"
    }
],

However, when I check the request received logs on the API, I see that my code is producing:

"content": [
    {
        "placename": "placeholder"
    },
    {
        "placecontent": "sample content"
    }
],

How can I stop the array record from being split in two?

Thanks.

EDIT: The desired output of the code is the equivalent of:

...&content[0][placename]=placeholder&content[0][placecontent]=sample%20content...
Was it helpful?

Solution

By default, HTTParty uses HashConversions to convert a Hash body to parameters:

Examples:

{ :name => "Bob",
  :address => {
    :street => '111 Ruby Ave.',
    :city => 'Ruby Central',
    :phones => ['111-111-1111', '222-222-2222']
  }
}.to_params
  #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111

Ruby Ave."

You can override this with your own convertor by using HTTParty.query_string_normalizer:

Override the way query strings are normalized. Helpful for overriding the default rails normalization of Array queries.

For a query:

get '/', :query => {:selected_ids => [1,2,3]}

The default query string normalizer returns:

/?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3

Let’s change it to this:

/?selected_ids=1&selected_ids=2&selected_ids=3

Pass a Proc to the query normalizer which accepts the yielded query.

@example Modifying Array query strings

class ServiceWrapper
  include HTTParty

  query_string_normalizer proc { |query|
    query.map do |key, value|
      value.map {|v| "#{key}=#{v}"}
    end.join('&')
  }
end

@param [Proc] normalizer custom query string normalizer. @yield [Hash, String] query string @yieldreturn [Array] an array that will later be joined with ‘&’

or simply pass it in your options:

response = HTTParty.post url, :body => 
    {"key"=>'XYZ123',
    "content"=>
        [{"placename"=>"placeholder",
        "placecontent"=>"sample content"}]},
    :query_string_normalizer => -> (h) { ... your own implementation here ...}

To get a serialization of a[1]=val1&a[2]=val2 instead of a[]=val1&a[]=val2 you can create your own HashConversions based on the current one

  class MyHashConversions

    def to_params(hash)
      params = hash.map { |k,v| normalize_param(k,v) }.join
      params.chop! # trailing &
      params
    end

    def normalize_param(key, value)
      param = ''
      stack = []

      if value.is_a?(Array)

        #### THE CHANGE IS HERE

        param << value.each_with_index.map { |element, i| normalize_param("#{key}[#{i}]", element) }.join

        ####
      elsif value.is_a?(Hash)
        stack << [key,value]
      else
        param << "#{key}=#{URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&"
      end

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

      param
    end
  end

The code above is not tested, but if it works, and is generic enough, you might consider forking the project and github, making the fix there, so it will work out of the box :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top