Rails 在渲染包含 Float::NAN 对象的哈希时返回无效的 JSON



我正在为 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

最新更新