Rails无法从jQuery正确解码JSON(数组变成带有整数键的哈希)


89

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

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

但是,如果我只是将数组作为AJAX调用的数据发送,则会得到:

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

而如果我只是发送一个简单的数组,它将起作用:

"shared_items"=>["entity_253"]

Rails为什么将数组更改为该奇怪的哈希?我想到的唯一原因是,Rails无法正确理解内容,因为这里没有类型(是否可以在jQuery调用中设置它?):

Processing by SharedListsController#create as 

谢谢!

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

Answers:


169

万一有人偶然发现这个问题并想要更好的解决方案,您可以在.ajax调用中指定“ contentType:'application / json'”选项,并让Rails正确地解析JSON对象,而不必将其变成带有全-的整数键哈希字符串值。

因此,总而言之,我的问题是:

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

导致Rails将事情解析为:

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

而这(注意:我们现在将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}]})
});

在Rails中产生一个漂亮的对象:

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

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


1
考虑到从Rails应用接收JQuery的JSON是很常见的情况,这对于Rails来说似乎不是正确的行为。是否有合理的理由说明为什么Rails会以这种方式行事?客户端JS代码似乎不得不不必要地跳了。
杰里米·伯顿

1
我认为在这种情况下,罪魁祸首是jQuery。iirc jQuery并未将Content-Type请求的设置为application/json,而是以html形式发送了数据?到目前为止,很难回想。将Content-Type设置为正确的值后,Rails运行正常,发送的数据为有效的JSON。
swajak 2014年

3
要注意的是,不仅@swajak字符串化了对象,还指定了contentType: 'application/json'
Roger Lam

1
感谢@RogerLam,我已经更新了解决方案以更好地描述我希望的
swajak

1
@swajak:非常感谢!你救了我的一天,真的!:)
Kulgar '16

12

这个问题有点老了,但是我今天自己解决了这个问题,这就是我想出的答案:我相信这是jQuery的错,但这只是在做自然而已。但是,我确实有一种解决方法。

给定以下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}]}

});

jQuery将发布的值将类似于以下内容(如果您在Firebug-of-choice中查看“请求”)将为您提供如下形式的表单数据:

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

如果您使用CGI.unencode,您将获得

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

我相信这是因为jQuery认为JSON中的那些键是表单元素名称,并且应该将它们视为您有一个名为“ user [name]”的字段。

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

无论如何,我通过以下方式构造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}])},
  }
});

这迫使jQuery认为此JSON是一个您想完全传递的,而不是它必须接受并将所有键转换为表单字段名称的Javascript对象。

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

但这没关系:

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

TL; DR:因此,解决方案是:在jQuery.ajax()调用的data参数中,{"data": JSON.stringify(my_object) }显式执行操作,而不是将JSON数组输入jQuery(错误地猜测要使用它做什么)。


下面是@swajak的更好的解决方案。
event_jr 2012年

7

我只是在Rails 4中遇到过这个问题。要明确回答您的问题(“为什么Rails会将数组更改为该奇怪的哈希值?”),请参见《Rails动作控制器指南》的 4.1节:

要发送值数组,请在键名称后附加一对空的方括号“ []”。

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

如果您无法控制客户端,则可以通过将哈希转换为数组来在服务器端解决此问题。这是我的操作方式:

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

1

如果使用强参数,以下方法可能会有所帮助

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

0

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


1
是的,考虑过,但是想知道在Rails中是否有“正确的方法”来执行此操作,而无需手动编码/解码。
aledalgrande

0

您是否只是想将JSON字符串放入Rails控制器操作中?

我不确定Rails对哈希做了什么,但是通过创建Javascript / JSON对象(而不是JSON字符串)并将其作为Ajax的data参数发送,您可能会解决问题并获得更多的运气呼叫。

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

如果您想通过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);
    }
});

注意上面的ajax代码片段,jQuery会明智地猜测dataType,尽管通常最好明确地指定它。

无论哪种方式,在控制器操作中,都可以获取通过params哈希传递的JSON对象,即

params[:shared_items]

例如,此操作将向您吐回您的json对象:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end

谢谢,但这就是我现在正在做的事情(请参阅更新),并且它不起作用。
aledalgrande 2011年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.