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

Posted

技术标签:

【中文标题】Rails 无法正确解码来自 jQuery 的 JSON(数组变成带有整数键的散列)【英文标题】:Rails not decoding JSON from jQuery correctly (array becoming a hash with integer keys) 【发布时间】:2011-09-18 15:25:39 【问题描述】:

每次我想用 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,结果相同。

【问题讨论】:

【参考方案1】:

如果有人偶然发现并想要更好的解决方案,您可以在 .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]

这适用于我在 Rails 3 中的 Ruby 1.9.3 上。

【讨论】:

这似乎不是 Rails 的正确行为,因为从 JQuery 接收 JSON 对于 Rails 应用程序来说是很常见的情况。为什么 Rails 会以这种方式运行,是否有合理的理由?似乎客户端 JS 代码不得不不必要地跳过。 我认为在上面的例子中罪魁祸首是 jQuery。 iirc jQuery 没有将请求的Content-Type 设置为application/json,而是将数据作为html 表单发送?很难回想这么远。在 Content-Type 设置为正确的值之后,Rails 工作得很好,并且发送的数据是有效的 JSON。 要注意不仅@swajak 对对象进行了字符串化,还指定了contentType: 'application/json' 感谢@RogerLam,我已经更新了解决方案以更好地描述我希望 @swajak:非常感谢!你拯救了我的一天,真的! :)【参考方案2】:

有点老的问题,但我今天自己解决了这个问题,这是我想出的答案:我相信这有点 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": JSON.stringify(my_object) ,而不是将 JSON 数组输入 jQuery(它猜错了你想用它做什么。

【讨论】:

提示更好的解决方案来自@swajak。【参考方案3】:

我刚刚在 Rails 4 中遇到了这个问题。要明确回答您的问题(“为什么 Rails 将数组更改为那个奇怪的哈希?”),请参阅Rails guide on Action Controllers 的第 4.1 节:

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

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

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

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

【讨论】:

【参考方案4】:

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

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】:

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

【讨论】:

可以考虑,但想知道在 Rails 中是否有“正确的方法”可以做到这一点,而无需手动编码/解码。【参考方案6】:

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

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

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

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

params[:shared_items]

例如此操作会将您的 json 对象吐回给您:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end

【讨论】:

谢谢,但这就是我现在正在做的事情(见更新),它不起作用。【参考方案7】:

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

【讨论】:

最好提供一些代码sn-p而不是放链接

以上是关于Rails 无法正确解码来自 jQuery 的 JSON(数组变成带有整数键的散列)的主要内容,如果未能解决你的问题,请参考以下文章

Rails jquery sortable无法正确保存位置

如何在 Firefox 中解码来自 jQuery $.ajax 请求的 XML 响应

由于rails资产,jQuery事件多次触发?

Ruby on Rails - Javascript/jQuery 到 Haml

Rails 5:如何防止来自 jQuery 的多次提交

来自 URL 查询字符串的 jQuery 自动完成搜索