Rails does not correctly decode JSON from jQuery (the array becomes a hash with integer keys)

Every time I want to POST an array of JSON objects with jQuery in Rails, I have this problem. If I build an array, I see that jQuery is doing its job correctly:

"shared_items"=>"[{\"entity_id\":\"253\",\"position\":1},{\"entity_id\":\"823\",\"position\":2}]" 

But if I just send the array as AJAX call data, I get:

 "shared_items"=>{"0"=>{"entity_id"=>"253", "position"=>"1"}, "1"=>{"entity_id"=>"823", "position"=>"2"}} 

If I just send a simple array, it works:

 "shared_items"=>["entity_253"] 

Why does Rails change the array to this weird hash? The only reason that comes to mind is that Rails cannot correctly understand the content because there is no type here (is there a way to set it in a jQuery call?):

 Processing by SharedListsController#create as 

Thank!

Update: I send the data as an array, not a string, and the array is created dynamically using the .push() function. Tried $.post and $.ajax , same result.

+81
json jquery arrays post ruby-on-rails
Jun 20 2018-11-12T00:
source share
7 answers

In case someone stumbles upon this and wants to get a better solution, you can specify the option "contentType:" application / json "in the .ajax call and have Rails correctly parse the JSON object without distorting it in hash with whole keys with all string values.

So, to summarize, my problem was this:

 $.ajax({ type : "POST", url : 'http://localhost:3001/plugin/bulk_import/', dataType: 'json', data : {"shared_items": [{"entity_id":"253","position":1}, {"entity_id":"823","position":2}]} }); 

led to Rails parsing things like:

 Parameters: {"shared_items"=>{"0"=>{"entity_id"=>"253", "position"=>"1"}, "1"=>{"entity_id"=>"823", "position"=>"2"}}} 

while this is (NOTE: we now pull together the javascript object and specify the type of content, so the rails will know how to parse our string):

 $.ajax({ type : "POST", url : 'http://localhost:3001/plugin/bulk_import/', dataType: 'json', contentType: 'application/json', data : JSON.stringify({"shared_items": [{"entity_id":"253","position":1}, {"entity_id":"823","position":2}]}) }); 

leads to a good object in Rails:

 Parameters: {"shared_items"=>[{"entity_id"=>"253", "position"=>1}, {"entity_id"=>"823", "position"=>2}]} 

This works for me in Rails 3, on Ruby 1.9.3.

+144
Jul 28 '12 at 2:11
source share

A bit old question, but I fought it myself today, and here is the answer I came up with: I think this is a bit of a jQuery error, but that it only does what is natural to it. However, I have a workaround.

Given the following jQuery ajax call:

 $.ajax({ type : "POST", url : 'http://localhost:3001/plugin/bulk_import/', dataType: 'json', data : {"shared_items": [{"entity_id":"253","position":1},{"entity_id":"823","position":2}]} }); 

The values ​​that jQuery publishes will look something like this (if you look at the query in your Firebug-of-choice), you will get form data that looks like this:

 shared_items%5B0%5D%5Bentity_id%5D:1 shared_items%5B0%5D%5Bposition%5D:1 

If you are CGI.unencode, you will receive

 shared_items[0][entity_id]:1 shared_items[0][position]:1 

I believe this is due to the fact that jQuery believes that these keys in your JSON are the names of the form elements and that they should treat them as if you had a field named "user [name]".

So, they enter your Rails application, Rails sees the brackets and builds a hash to store the very internal key of the field name ("1", which "jQuery" added ").

In any case, I circumvented this behavior by constructing my ajax call as follows:

 $.ajax({ type : "POST", url : 'http://localhost:3001/plugin/bulk_import/', dataType: 'json', data : {"data": JSON.stringify({"shared_items": [{"entity_id":"253","position":1},{"entity_id":"823","position":2}])}, } }); 

Which makes jQuery think that this JSON is the value you want to pass in full, and not the Javascript object that it should accept, and turn all the keys into form field names.

However, this means that the Rails side is slightly different, because you need to explicitly decode JSON in the [: data] parameters.

But it normal:

 ActiveSupport::JSON.decode( params[:data] ) 

TL; DR: So, the solution: in the data parameter for your jQuery.ajax () call, explicitly {"data": JSON.stringify(my_object) } , instead of {"data": JSON.stringify(my_object) } JSON array to jQuery (where it incorrectly assumes you want to do this.

+11
Aug 10 '11 at 10:20
source share

I just ran into this problem with Rails 4. To explicitly answer your question (β€œWhy does Rails change the array to this weird hash?”), See Section 4.1 of the Rails Guide to Action Controllers :

To send an array of values, add an empty pair of square brackets "[]" to the key name.

The problem is that jQuery formats the query with explicit array indices and not with empty square brackets. So, for example, instead of sending shared_items[]=1&shared_items[]=2 it sends shared_items[0]=1&shared_items[1]=2 . Rails sees the array indices and interprets them as hash keys, not array indices, turning the request into a strange Ruby hash code: { shared_items: { '0' => '1', '1' => '2' } } .

If you do not have control over the client, you can fix this problem on the server side by converting the hash to an array. Here is how I did it:

 shared_items = [] params[:shared_items].each { |k, v| shared_items << v } 
+5
Aug 25 '14 at 2:22
source share

the following method may be useful if you use strong parameters

 def safe_params values = params.require(:shared_items) values = items.values if items.keys.first == '0' ActionController::Parameters.new(shared_items: values).permit(shared_items: [:entity_id, :position]).require(:shared_items) end 
+1
Nov 04 '14 at 19:06
source share

Have you considered doing parsed_json = ActiveSupport::JSON.decode(your_json_string) ? If you submit the material in another way, you can use .to_json to serialize the data.

0
Jun 20 '11 at 12:24
source share

Are you just trying to get a JSON string into a Rails controller action?

I'm not sure what Rails does with a hash, but you can get around this problem and get more luck by creating a Javascript / JSON object (as opposed to a JSON string) and sending this as a data parameter for your Ajax call.

 myData = { "shared_items": [ { "entity_id": "253", "position": 1 }, { "entity_id": "823", "position": 2 } ] }; 

If you want to send this via ajax, you will do something like this:

 $.ajax({ type: "POST", url: "my_url", // be sure to set this in your routes.rb data: myData, success: function(data) { console.log("success. data:"); console.log(data); } }); 

Note the ajax snippet above, jQuery will make an intelligent assumption about the dataType type, although it is usually useful to specify it explicitly.

In any case, in the action of your controller, you can get the JSON object that you passed with the params hash, i.e.

 params[:shared_items] 

eg. this action will return your json object to you:

 def reply_in_json @shared = params[:shared_items] render :json => @shared end 
0
Jun 20 '11 at 13:20
source share

Use rack-jquery-params gem (disclaimer: I'm the author). It fixes your problem with arrays becoming hashes with integer keys.

0
Jan 19 '15 at 1:53
source share



All Articles