【问题标题】:Rails returns invalid JSON when rendering hash containing Float::NAN objects渲染包含 Float::NAN 对象的哈希时,Rails 返回无效 JSON
【发布时间】:2012-06-18 20:09:23
【问题描述】:

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

{
  "value": NaN
}

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

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

我尝试将散列扁平化为字符串并将其转换回散列。但我一直遇到解析错误。

我该如何解决这个问题?


编辑:

简单来说,哈希是这样的:

{ "value" => Float::NAN }

编辑 2:

我需要把上面的hash转换成下面的json:

{
  "value": "NaN"
}

【问题讨论】:

  • “包含 NaN 对象的 Ruby 哈希”是什么意思? Ruby 中没有所谓的 NaN。实际的哈希是什么样的?
  • 这似乎是 9 个月前用 github.com/rails/rails/commit/… 修复的...我使用的是 Rails 3.2.3。为什么我还看到这个?

标签: ruby-on-rails ruby json


【解决方案1】:

啊,我想通了 - 大约 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

【讨论】:

【解决方案2】:

查看甜蜜的ActiveModel::Serializer(现在与 rails 捆绑在一起)。有了它,您可以自定义控制器序列化模型的方式。

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 控制台中工作。我有a quick 'n dirty module,它用ActiveModel::Serializer 中的方法覆盖ActiveModel::Serializers 中的方法,因此事情更加一致。

【讨论】:

  • 我想你会发现使用== 对抗Float::NAN 是行不通的。您必须检查该值是否为Float,然后调用#nan?
  • 它在控制台中运行良好。只需阅读文档:P BlarkSerializer.new(Blark.first).serializable_hash.to_json
【解决方案3】:

我遇到了类似的问题,尽管在 Rails 3.2.8 上,但没有发现根据 Brian 的评论解决问题(他建议的代码 sn-p 也通过关于 NilClass::AS_JSON 未定义的错误)。

但是,我确实发现了这个有趣的讨论:

https://github.com/flori/json/pull/145

这似乎表明 to_json / parse 对于跨语言兼容性更好,就像通过网络通信时一样,但 JSON.dump / JSON.load 为仅在 Ruby 中工作提供了更好的结果。

我发现使用 dump/load 而不是 to_json/parse 修复了我序列化和反序列化带有 NaN 的哈希的能力。

【讨论】:

    【解决方案4】:

    您使用的是什么 JSON 生成器?好像坏了。

    当我尝试使用 json gem 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)
    }
    

    【讨论】:

    • 认真的吗? Ruby on Rails 不能将 NaN 解析为“NaN”吗?这是为什么呢?
    【解决方案5】:

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

    begin
      tmp = Float::NAN
      Float::NAN = 'NaN'
      { "value" => Float::NAN }.to_json
    ensure
      Float::NAN = tmp
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-12
      • 1970-01-01
      • 2013-07-17
      • 2012-11-14
      相关资源
      最近更新 更多