2

我正在为 Rails 控制器调用返回 JSON。在控制器中,我需要从包含 NaN 对象的 Ruby 哈希生成 JSON。返回 JSON 时浏览器显示错误:

{
  "value": NaN
}

错误:第 1 行的解析错误:“值”:NaN

我做了一些快速研究,似乎 NaN 不是有效的 JSON 对象,因此应该用引号括起来 - “NaN”。Rails JSON 解析器不符合标准 JSON 格式并将 NaN 对象包装在引号中,这似乎很愚蠢。

我试图将散列展平为字符串并将其转换回散列。但是我一直在遇到解析错误。

我该如何解决这个问题?


编辑:

为了简单的问题,哈希看起来像这样:

{ "value" => Float::NAN }

编辑2:

我需要将上面的哈希转换为以下 json:

{
  "value": "NaN"
}
4

5 回答 5

5

啊,我想通了——大约 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
于 2012-06-18T22:45:03.380 回答
2

查看甜点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事情更加一致。

于 2012-06-18T22:32:34.183 回答
2

我遇到了一个类似的问题,尽管在 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 的哈希的能力。

于 2012-09-20T14:46:08.393 回答
1

您使用的是什么 JSON 生成器?它似乎坏了。

当我尝试使用jsongem v1.6.4 将您的哈希转换为 JSON 时出现异常:

require 'json'

hash = { "value" => Float::NAN }
hash.to_json

# => JSON::GeneratorError: 778: NaN not allowed in JSON

如果您确实需要支持序列化 NaN 值,则需要先替换散列中的 Float::NAN 值,然后再将其转换为 JSON:

json_string = hash.inject({}) { |h, (k, v)|
  v = "NaN" if v.is_a?(Float) && v.nan?
  h.merge(k => v)
}.to_json

# => "{\"value\":\"NaN\"}"

解析后不要忘记再次用 Float::NAN 替换 "NaN" 字符串:

hash = JSON.parse(json_string).inject({}) { |h, (k, v)|
  v = Float::NAN if v == "NaN"
  h.merge(k => v)
}
于 2012-06-18T21:56:02.867 回答
0

这个 hack 不需要 rails 并且在 vanilla 2.2.x ruby​​ 中工作,但要小心包含下面的“确保”块。您可以(正确地)期待警告,因此请谨慎使用。

begin
  tmp = Float::NAN
  Float::NAN = 'NaN'
  { "value" => Float::NAN }.to_json
ensure
  Float::NAN = tmp
end
于 2016-05-19T20:54:54.967 回答