我正在为 Rails 控制器调用返回 JSON。在控制器中,我需要从包含 NaN 对象的 Ruby 哈希生成 JSON。返回 JSON 时,浏览器会显示错误:
{
"value": NaN
}
错误:第 1 行上的解析错误:"值":NaN
我做了一些快速的研究,似乎 NaN 不是一个有效的 JSON 对象,因此应该用引号括起来 - "NaN"。Rails JSON 解析器不符合标准的 JSON 格式并将 NaN 对象包装在引号中,这似乎很愚蠢。
我试图将哈希扁平化为字符串并将其转换回哈希。但是我一直收到解析错误。
我该如何解决这个问题?
编辑:
简单地说,哈希看起来像这样:
{ "value" => Float::NAN }
编辑2:
我需要将上面的哈希转换为以下 json:
{
"value": "NaN"
}
我想通了 - 这是大约 10 个月前在 Rails 主分支中修复的。(https://github.com/rails/rails/commit/7b53d1372df4bbc7a539b5e493ff1491ec460397)
要为您的 Rails 3.2.3 <修复它,只需添加以下代码:>
class Float
def as_json(options = nil) finite? ? self : NilClass::AS_JSON end #:nodoc:
end
查看甜蜜的ActiveModel::Serializer
(现在与导轨捆绑在一起)。 有了它,您可以自定义控制器序列化模型的方式。
class Blark < ActiveRecord::Base
attr_accessor :value
end
class BlarkSerializer < ActiveModel::Serializer
attributes :value
def value
(@bject.value.is_a?(Float) && @object.value.nan?) ? "NaN" : @object.value
end
end
警告
这仅适用于控制器的render
方法。 不要指望它在 rails 控制台中工作。 我有一个快速的脏模块,它覆盖了ActiveModel::Serializers
中的方法和ActiveModel::Serializer
中的方法,因此事情更加一致。
我遇到了类似的问题,尽管在 Rails 3.2.8 上,但没有根据 Brian 的评论找到要修复的问题(他建议的代码片段也通过关于 NilClass::AS_JSON 的错误未定义)。
但是,我确实发现了这个有趣的讨论:
https://github.com/flori/json/pull/145
这似乎表明to_json/parse对于跨语言兼容性更好,就像通过网络进行通信一样,但是JSON.dump/JSON.load仅在Ruby中工作时提供了更好的结果。
我发现使用转储/加载而不是 to_json/parse 修复了我序列化和反序列化包含 NaN 的哈希的能力。
你用的是什么JSON生成器?它似乎坏了。
当我尝试使用 gem v1.6.4 将您的哈希转换为 JSON 时,我遇到了json
异常:
require 'json'
hash = { "value" => Float::NAN }
hash.to_json
# => JSON::GeneratorError: 778: NaN not allowed in JSON
如果您确实需要支持序列化 NaN 值,则需要在将哈希转换为 JSON 之前替换哈希中的 Float::NAN 值:
json_string = hash.inject({}) { |h, (k, v)|
v = "NaN" if v.is_a?(Float) && v.nan?
h.merge(k => v)
}.to_json
# => "{"value":"NaN"}"
不要忘记在解析后再次将"NaN"字符串替换为 Float::NAN:
hash = JSON.parse(json_string).inject({}) { |h, (k, v)|
v = Float::NAN if v == "NaN"
h.merge(k => v)
}
这个技巧不需要轨道,适用于原版 2.2.x ruby,但请注意包含如下所示的"确保"块。 您可以(正确)收到警告,因此请谨慎使用。
begin
tmp = Float::NAN
Float::NAN = 'NaN'
{ "value" => Float::NAN }.to_json
ensure
Float::NAN = tmp
end