【发布时间】:2011-07-04 16:26:12
【问题描述】:
在我的控制器中,以下工作(打印“oké”)
puts obj.inspect
但这不会(呈现“ok\u00e9”)
render :json => obj
显然to_json 方法转义了 unicode 字符。有没有办法防止这种情况发生?
【问题讨论】:
标签: ruby-on-rails ruby json encoding
在我的控制器中,以下工作(打印“oké”)
puts obj.inspect
但这不会(呈现“ok\u00e9”)
render :json => obj
显然to_json 方法转义了 unicode 字符。有没有办法防止这种情况发生?
【问题讨论】:
标签: ruby-on-rails ruby json encoding
将 \uXXXX 代码设置回 utf-8:
json_string.gsub!(/\\u([0-9a-z]{4})/) {|s| [$1.to_i(16)].pack("U")}
【讨论】:
你可以通过猴子补丁来防止它 muu 提到的方法太短了。将以下内容放入 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。
【讨论】:
如果你挖掘源代码,你最终会找到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 表示法。处理 JSON 的任何东西都应该接受十六进制编码的 UTF-8,但您始终可以对 JSON(或修改后的 JSON 转义器中的猴子补丁)进行后处理,以在必要时将 \uXXXX 表示法转换为原始 UTF-8。
我同意强制 JSON 为 7bit-clean 有点假,但你去吧。
简短回答:不。
【讨论】:
Rails2.3.11/Ruby1.8 中的其他方法没有将字符转义为 unicode,因此我使用了以下方法:
render :json => JSON::dump(obj)
【讨论】:
这是正确的编码。 JSON要求转义 Unicode 字符,但 JSON 库通常会生成仅包含 7 位 ASCII 字符的输出,以避免在传输过程中出现任何潜在的编码问题。
任何 JSON 解释器都可以使用该字符串并重现原始字符串。要查看实际情况,只需在浏览器的地址栏中输入 javascript:alert("ok\u00e9")。
【讨论】:
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
【讨论】:
我有一个非常棘手的方法来解决这个问题。好吧,如果to_json不允许你有正确的代码,那你可以直接尝试写:
render text: tags
render json: tags 或render json: tags.to_json 将始终自动转换编码样式,但如果您使用render text:tags,则字符串将保持原样。而且我认为 jQuery 仍然可以识别数据。
【讨论】: