Search code examples
ruby-on-railsjsonhttparty

Can I reduce duplication in the serialization of a ruby hash?


I have a hash of the format

{com: 1234, users: [{nid: 3, sets: [1,2,3,4]}, {nid: 4, sets: [5,6,7,8]}]}

which I am sending to a remote server. I am using the HTTParty gem to do this. The code looks like this

class Api
    include HTTParty
    attr_accessor :headers

    def initialize
      @headers = { 'Content-Type' => 'application/json' }
    end
    
    def post_com(hsh)
      response = self.class.post('some_url', query: hsh, headers: headers, format: :plain)
    end
end

When I do

api = Api.new.post_com({com: 1234, users: [{nid: 3, sets: [1,2,3,4]}, {nid: 4, sets: [5,6,7,8]}]}

at the remote server, the hash is being sent in the following format

POST "/some_url?com=1234&users[][nid]=3&users[][sets][]=1&users[][sets][]=2&users[][sets][]=3&users[][sets][]=4&users[][nid]=4&users[][sets][]=5&users[][sets][]=6&users[][sets][]=7&users[][sets][]=8

This means for every entry in set, duplicate characeters users[][sets][] are being sent. In operation, there can be many entries in set, and the result is the server rejects the post as having too many characters.

Is there anyway I can have the hash serialized with far less duplication. For instance if I just do

{com: 1234, users: [{nid: 3, sets: [1,2,3,4]}, {nid: 4, sets: [5,6,7,8]}]}.to_json

I receive

"{\"com\":1234,\"users\":[{\"nid\":3,\"sets\":[1,2,3,4]},{\"nid\":4,\"sets\":[5,6,7,8]}]}"

which has far fewer characters.


Solution

  • HTTParty, by default, converts the :query hash into what it calls 'rails style query parameters':

    For a query:
    get '/', query: {selected_ids: [1,2,3]}

    The default query string looks like this:
    /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3

    Since you are doing a POST, it is possible/preferable to send your hash in the body of the request rather than in the query string.

    def post_com(hsh)
      self.class.post('some_url', body: hsh.to_json, headers: headers, format: :plain)
    end
    

    This has the advantage that it doesn't do any transformation of the payload and the query string length limit doesn't apply anyway.

    For the record, you can disable the default 'rails style' encoding like this:

    class Api
      include HTTParty
      disable_rails_query_string_format
    
      ...
    end
    

    You can also roll your own custom query string normalizer by passing a Proc to query_string_normalizer.