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(数组变成带有整数键的散列)的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Firefox 中解码来自 jQuery $.ajax 请求的 XML 响应