Rails 无法正确解码来自 jQuery 的 JSON(数组变成带有整数键的散列)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6410810/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 20:49:09  来源:igfitidea点击:

Rails not decoding JSON from jQuery correctly (array becoming a hash with integer keys)

jqueryruby-on-railsarraysjsonpost

提问by aledalgrande

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 POST 一组 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 $.postand $.ajax, same result.

更新:我将数据作为数组而不是字符串发送,并且该数组是使用该.push()函数动态创建的。尝试使用$.post$.ajax,结果相同。

回答by swajak

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.

这在 Ruby 1.9.3 上的 Rails 3 中对我有用。

回答by RyanWilcox

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-of-choice 中的请求)将为您提供如下所示的表单数据:

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 valuethat you want to pass, entirely, and nota 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.

TL;DR:所以,解决方案是:在你的 jQuery.ajax() 调用的数据参数中,{"data": JSON.stringify(my_object) }明确地做,而不是将 JSON 数组提供给 jQuery(它错误地猜测你想用它做什么。

回答by jessepinho

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 指南中关于 Action Controllers 的第 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。导轨看到阵列索引和它们解释为混杂键而不是数组索引,把请求转换为怪异红宝石散列:{ shared_items: { '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
}

回答by Eugene

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

回答by Caleb Clark

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

使用rack-jquery-paramsgem(免责声明:我是作者)。它解决了数组变成带有整数键的散列的问题。

回答by Michael De Silva

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_jsonto serialise the data.

你考虑过做parsed_json = ActiveSupport::JSON.decode(your_json_string)吗?如果您以另一种方式发送内容,则可以使用它.to_json来序列化数据。

回答by australis

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 将对 dataType 进行智能猜测,尽管显式指定它通常是好的。

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