JSON 编码错误地转义(Rails 3、Ruby 1.9.2)

问题描述 投票:0回答:7

在我的控制器中,可以执行以下操作(打印“oké”)

puts obj.inspect

但这不是(呈现“ok\u00e9”)

render :json => obj

显然

to_json
方法转义了 unicode 字符。有办法阻止这种情况吗?

ruby-on-rails ruby json encoding
7个回答
34
投票

将 \uXXXX 代码设置回 utf-8:

json_string.gsub!(/\\u([0-9a-z]{4})/) {|s| [$1.to_i(16)].pack("U")}

21
投票

你可以通过猴子补丁来防止它,mu提到的方法太短了。将以下内容放入 config/initializers/patches.rb (或用于修补内容的类似文件)中,然后重新启动 Rails 进程以使更改生效。

module ActiveSupport::JSON::Encoding
  class << self
    def escape(string)
      if string.respond_to?(:force_encoding)
        string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
      end
      json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }
      json = %("#{json}")
      json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
      json
    end
  end
end

请注意,无法保证该补丁适用于 ActiveSupport 的未来版本。写这篇文章时使用的版本是3.1.3。


15
投票

如果你深入研究源代码,你最终会发现

ActiveSupport::JSON::Encoding
escape
方法:

def escape(string)
  if string.respond_to?(:force_encoding)
    string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
  end
  json = string.
    gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
    gsub(/([\xC0-\xDF][\x80-\xBF]|
           [\xE0-\xEF][\x80-\xBF]{2}|
           [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
    s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
  }
  json = %("#{json}")
  json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
  json
end

各种

gsub
调用将非 ASCII UTF-8 强制转换为您所看到的
\uXXXX
表示法。十六进制编码的 UTF-8 应该可以接受任何处理 JSON 的东西,但如果需要,您始终可以对 JSON(或修改后的 JSON 转义器中的猴子补丁)进行后处理,以将
\uXXXX
表示法转换为原始 UTF-8。

我同意强制 JSON 为 7 位干净有点假,但你就知道了。

简短回答:不。


13
投票

Rails2.3.11/Ruby1.8
中的其他方法没有将字符转义为unicode,所以我使用了以下方法:

render :json => JSON::dump(obj)

9
投票

这是正确的编码。 JSON 不要求转义 Unicode 字符,但 JSON 库通常会生成仅包含 7 位 ASCII 字符的输出,以避免传输过程中出现任何潜在的编码问题。

任何 JSON 解释器都能够使用该字符串并重现原始字符串。要查看实际效果,只需在浏览器的地址栏中输入

javascript:alert("ok\u00e9")


4
投票

render :json 如果对象不是字符串,则会调用 .to_json 。您可以通过执行以下操作来避免此问题:

render :json => JSON.generate(obj)

这将直接传递字符串,因此避免调用 ActiveSupport 的 to_json。

另一种方法是覆盖您正在序列化的对象上的 to_json ,因此在这种情况下,您可以执行以下操作:

class Foo < ActiveRecord::Base
  def to_json(options = {})
    JSON.generate(as_json)
  end
end

如果您使用 ActiveModelSerializers,您可以通过覆盖序列化器中的 to_json 来解决此问题:

# controller
respond_with foo, :serializer => MySerializer

# serializer
attributes :bar, :baz

def to_json(options = {})
  JSON.generate(serializable_hash)
end

0
投票

我有一个非常棘手的方法来解决这个问题。好吧,如果

to_json
不允许你有正确的代码,那么你可以直接尝试写:

render text: tags

render json: tags
render json: tags.to_json
将始终自动传输编码风格,但如果您使用
render text:tags
,则字符串将保持原样。而且我认为 jQuery 仍然可以识别数据。

© www.soinside.com 2019 - 2024. All rights reserved.