Rails不能正确地从jQuery解码JSON(数组变成带整数键的散列)

时间:2022-10-17 09:34:42

Every time I want to POST an array of JSON objects with jQuery to Rails, I have this problem. If I stringify the array I can see that jQuery is doing its work correctly:

每当我想用jQuery向Rails发布JSON对象数组时,我都会遇到这个问题。如果我对数组进行字符串化,我可以看到jQuery的工作是正确的:

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

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

但如果我将数组作为AJAX调用的数据发送出去,我得到:

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

Whereas if I just send a plain array it works:

然而,如果我只发送一个简单的数组,它会工作:

"shared_items"=>["entity_253"]

Why is Rails changing the array to that strange hash? The only reason that comes to mind is that Rails can't correctly understand the contents because there is no type here (is there a way to set it in the jQuery call?):

为什么Rails将数组更改为那个奇怪的散列?我想到的唯一原因是Rails不能正确地理解内容,因为这里没有类型(有办法在jQuery调用中设置它吗?)

Processing by SharedListsController#create as 

Thank you!

谢谢你!

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

更新:我将数据作为数组发送,而不是字符串,数组是使用.push()函数动态创建的。试着美元。邮报》和美元。ajax,相同的结果。

7 个解决方案

#1


153  

In case someone stumbles upon this and wants a better solution, you can specify the "contentType: 'application/json'" option in the .ajax call and have Rails properly parse the JSON object without garbling it into integer-keyed hashes with all-string values.

如果有人遇到这种情况并想要更好的解决方案,您可以在.ajax调用中指定“contentType: 'application/json'”选项,并让Rails正确地解析json对象,而不会将其与全字符串值混合在一起。

So, to summarize, my problem was that 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}]}
});

resulted in Rails parsing things as:

导致Rails解析的事情如下:

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

whereas this (NOTE: we're now stringifying the javascript object and specifying a content type, so rails will know how to parse our string):

鉴于此(注意:我们现在对javascript对象进行了字符串化并指定了内容类型,因此rails将知道如何解析我们的字符串):

$.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}]})
});

results in a nice object in Rails:

结果在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.

这适用于Rails 3, Ruby 1.9.3。

#2


11  

Slightly old question, but I fought with this myself today, and here's the answer I came up with: I believe this is slightly jQuery's fault, but that it's only doing what is natural to it. I do, however, have a workaround.

这个问题有点老了,但我今天自己也遇到了这个问题,我的答案是:我认为这有点jQuery的错,但它只做对它来说很自然的事情。然而,我确实有一个变通的办法。

Given the following jQuery ajax call:

给定以下jQuery ajax调用:

$.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 jQuery will post will look something like this (if you look at the Request in your Firebug-of-choice) will give you form data that looks like:

jQuery将发布的值如下所示(如果您查看您所选择的firebug中的请求),将为您提供如下形式的数据:

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

If you CGI.unencode that you'll get

如果你CGI。你会得到unencode

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

I believe this is because jQuery thinks that those keys in your JSON are form element names, and that it should treat them as if you had a field named "user[name]".

我认为这是因为jQuery认为JSON中的键是表单元素名,它应该将它们视为一个名为“user[name]”的字段。

So they come into your Rails app, Rails sees the brackets, and constructs a hash to hold the innermost key of the field name (the "1" that jQuery "helpfully" added).

因此,它们进入您的Rails应用程序,Rails会看到括号,并构造一个散列来保存字段名最内层的键(jQuery“有用地”添加了“1”)。

Anyway, I got around this behavior by constructing my ajax call the following way;

不管怎样,我通过下面的方法构建了ajax调用来解决这个问题;

$.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 forces jQuery to think that this JSON is a value that you want to pass, entirely, and not a Javascript object it must take and turn all the keys into form field names.

这迫使jQuery认为这个JSON是一个您想要传递的值,而不是一个Javascript对象,它必须将所有的键转换成表单字段名。

However, that means things are a little different on the Rails side, because you need to explicitly decode the JSON in params[:data].

然而,这意味着在Rails方面有些不同,因为您需要显式地在params[:data]中解码JSON。

But that's OK:

但没关系,

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

TL;DR: So, the solution is: in the data parameter to your jQuery.ajax() call, do {"data": JSON.stringify(my_object) } explicitly, instead of feeding the JSON array into jQuery (where it guesses wrongly what you want to do with it.

解决方案是:在jQuery.ajax()调用的数据参数中,显式地执行{“data”:JSON.stringify(my_object)},而不是将JSON数组错误地输入jQuery(在jQuery中,它猜测您想要使用它做什么)。

#3


6  

I just ran into this issue with Rails 4. To explicitly answer your question ("Why is Rails changing the array to that strange hash?"), see section 4.1 of the Rails guide on Action Controllers:

我刚刚在Rails 4中遇到了这个问题。要明确地回答您的问题(“为什么Rails将数组更改为那个奇怪的散列?”),请参阅《关于动作控制器的Rails指南》第4.1节:

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

要发送一个值数组,请将一对方括号“[]”附加到键名。

The problem is, jQuery formats the request with explicit array indices, rather than 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 rather than array indices, turning the request into a weird Ruby hash: { shared_items: { '0' => '1', '1' => '2' } }.

问题是,jQuery使用显式数组索引来格式化请求,而不是使用空方括号。例如,它不是发送shared_items[]=1&shared_items[]=2,而是发送shared_items[0]=1&shared_items[1]=2。Rails看到数组索引并将它们解释为散列键而不是数组索引,将请求转换为一个奇怪的Ruby散列:{'0' => '1','1' => '2'}。

If you don't have control of the client, you can fix this problem on the server side by converting the hash to an array. Here's how I did it:

如果您无法控制客户端,您可以通过将散列转换为数组在服务器端解决这个问题。我是这样做的:

shared_items = []
params[:shared_items].each { |k, v|
  shared_items << v
}

#4


1  

following method could be helpful 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

#5


0  

Have you considered doing parsed_json = ActiveSupport::JSON.decode(your_json_string)? If you're sending stuff the other way about you can use .to_json to serialise the data.

您是否考虑过执行parsed_json = ActiveSupport::JSON.decode(your_json_string)?如果你用另一种方式发送数据,你可以使用。to_json来序列化数据。

#6


0  

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

您是否正在尝试将JSON字符串转换为Rails控制器操作?

I'm not sure what Rails is doing with the hash, but you might get around the problem and have more luck by creating a Javascript/JSON object (as opposed to a JSON string) and sending that through as the data parameter for your Ajax call.

我不确定Rails是如何处理这个散列的,但是您可以通过创建一个Javascript/JSON对象(与JSON字符串相反)并将其作为Ajax调用的数据参数发送过去,从而绕过这个问题,获得更多的幸运。

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

If you wanted to send this via ajax then you would do something like this:

如果你想通过ajax发送,你可以这样做:

$.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 with the ajax snippet above, jQuery will make an intelligent guess on the dataType, although it's usually good to specify it explicitly.

在上面的ajax代码片段中,jQuery将对数据类型进行智能的猜测,尽管通常很好地指定它。

Either way, in your controller action, you can get the JSON object you passed with the params hash, i.e.

无论哪种方式,在控制器操作中,您都可以获得使用params散列传递的JSON对象,即。

params[:shared_items]

E.g. this action will spit your json object back at you:

这个动作会把你的json对象吐到你身上:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end

#7


0  

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

使用rack-jquery-params gem(免责声明:我是作者)。它修复了数组变成带整数键的哈希的问题。

#1


153  

In case someone stumbles upon this and wants a better solution, you can specify the "contentType: 'application/json'" option in the .ajax call and have Rails properly parse the JSON object without garbling it into integer-keyed hashes with all-string values.

如果有人遇到这种情况并想要更好的解决方案,您可以在.ajax调用中指定“contentType: 'application/json'”选项,并让Rails正确地解析json对象,而不会将其与全字符串值混合在一起。

So, to summarize, my problem was that 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}]}
});

resulted in Rails parsing things as:

导致Rails解析的事情如下:

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

whereas this (NOTE: we're now stringifying the javascript object and specifying a content type, so rails will know how to parse our string):

鉴于此(注意:我们现在对javascript对象进行了字符串化并指定了内容类型,因此rails将知道如何解析我们的字符串):

$.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}]})
});

results in a nice object in Rails:

结果在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.

这适用于Rails 3, Ruby 1.9.3。

#2


11  

Slightly old question, but I fought with this myself today, and here's the answer I came up with: I believe this is slightly jQuery's fault, but that it's only doing what is natural to it. I do, however, have a workaround.

这个问题有点老了,但我今天自己也遇到了这个问题,我的答案是:我认为这有点jQuery的错,但它只做对它来说很自然的事情。然而,我确实有一个变通的办法。

Given the following jQuery ajax call:

给定以下jQuery ajax调用:

$.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 jQuery will post will look something like this (if you look at the Request in your Firebug-of-choice) will give you form data that looks like:

jQuery将发布的值如下所示(如果您查看您所选择的firebug中的请求),将为您提供如下形式的数据:

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

If you CGI.unencode that you'll get

如果你CGI。你会得到unencode

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

I believe this is because jQuery thinks that those keys in your JSON are form element names, and that it should treat them as if you had a field named "user[name]".

我认为这是因为jQuery认为JSON中的键是表单元素名,它应该将它们视为一个名为“user[name]”的字段。

So they come into your Rails app, Rails sees the brackets, and constructs a hash to hold the innermost key of the field name (the "1" that jQuery "helpfully" added).

因此,它们进入您的Rails应用程序,Rails会看到括号,并构造一个散列来保存字段名最内层的键(jQuery“有用地”添加了“1”)。

Anyway, I got around this behavior by constructing my ajax call the following way;

不管怎样,我通过下面的方法构建了ajax调用来解决这个问题;

$.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 forces jQuery to think that this JSON is a value that you want to pass, entirely, and not a Javascript object it must take and turn all the keys into form field names.

这迫使jQuery认为这个JSON是一个您想要传递的值,而不是一个Javascript对象,它必须将所有的键转换成表单字段名。

However, that means things are a little different on the Rails side, because you need to explicitly decode the JSON in params[:data].

然而,这意味着在Rails方面有些不同,因为您需要显式地在params[:data]中解码JSON。

But that's OK:

但没关系,

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

TL;DR: So, the solution is: in the data parameter to your jQuery.ajax() call, do {"data": JSON.stringify(my_object) } explicitly, instead of feeding the JSON array into jQuery (where it guesses wrongly what you want to do with it.

解决方案是:在jQuery.ajax()调用的数据参数中,显式地执行{“data”:JSON.stringify(my_object)},而不是将JSON数组错误地输入jQuery(在jQuery中,它猜测您想要使用它做什么)。

#3


6  

I just ran into this issue with Rails 4. To explicitly answer your question ("Why is Rails changing the array to that strange hash?"), see section 4.1 of the Rails guide on Action Controllers:

我刚刚在Rails 4中遇到了这个问题。要明确地回答您的问题(“为什么Rails将数组更改为那个奇怪的散列?”),请参阅《关于动作控制器的Rails指南》第4.1节:

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

要发送一个值数组,请将一对方括号“[]”附加到键名。

The problem is, jQuery formats the request with explicit array indices, rather than 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 rather than array indices, turning the request into a weird Ruby hash: { shared_items: { '0' => '1', '1' => '2' } }.

问题是,jQuery使用显式数组索引来格式化请求,而不是使用空方括号。例如,它不是发送shared_items[]=1&shared_items[]=2,而是发送shared_items[0]=1&shared_items[1]=2。Rails看到数组索引并将它们解释为散列键而不是数组索引,将请求转换为一个奇怪的Ruby散列:{'0' => '1','1' => '2'}。

If you don't have control of the client, you can fix this problem on the server side by converting the hash to an array. Here's how I did it:

如果您无法控制客户端,您可以通过将散列转换为数组在服务器端解决这个问题。我是这样做的:

shared_items = []
params[:shared_items].each { |k, v|
  shared_items << v
}

#4


1  

following method could be helpful 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

#5


0  

Have you considered doing parsed_json = ActiveSupport::JSON.decode(your_json_string)? If you're sending stuff the other way about you can use .to_json to serialise the data.

您是否考虑过执行parsed_json = ActiveSupport::JSON.decode(your_json_string)?如果你用另一种方式发送数据,你可以使用。to_json来序列化数据。

#6


0  

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

您是否正在尝试将JSON字符串转换为Rails控制器操作?

I'm not sure what Rails is doing with the hash, but you might get around the problem and have more luck by creating a Javascript/JSON object (as opposed to a JSON string) and sending that through as the data parameter for your Ajax call.

我不确定Rails是如何处理这个散列的,但是您可以通过创建一个Javascript/JSON对象(与JSON字符串相反)并将其作为Ajax调用的数据参数发送过去,从而绕过这个问题,获得更多的幸运。

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

If you wanted to send this via ajax then you would do something like this:

如果你想通过ajax发送,你可以这样做:

$.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 with the ajax snippet above, jQuery will make an intelligent guess on the dataType, although it's usually good to specify it explicitly.

在上面的ajax代码片段中,jQuery将对数据类型进行智能的猜测,尽管通常很好地指定它。

Either way, in your controller action, you can get the JSON object you passed with the params hash, i.e.

无论哪种方式,在控制器操作中,您都可以获得使用params散列传递的JSON对象,即。

params[:shared_items]

E.g. this action will spit your json object back at you:

这个动作会把你的json对象吐到你身上:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end

#7


0  

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

使用rack-jquery-params gem(免责声明:我是作者)。它修复了数组变成带整数键的哈希的问题。