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

时间:2011-02-25 23:52:28

标签: ruby-on-rails ruby json encoding

在我的控制器中,以下作品(打印“oké”)

puts obj.inspect

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

render :json => obj

显然to_json方法会转义unicode字符。有没有办法防止这种情况发生?

7 个答案:

答案 0 :(得分:34)

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

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

答案 1 :(得分:21)

你可以通过猴子修补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。

答案 2 :(得分:15)

如果您仔细查看来源,最终会转到ActiveSupport::JSON::Encodingescape方法:

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 escaper中的猴子补丁)进行后处理,以便在必要时将\uXXXX表示法转换为原始UTF-8。 / p>

我同意强制JSON为7bit-clean有点假,但你去了。

简答:不。

答案 3 :(得分:11)

字符未使用Rails2.3.11/Ruby1.8中的其他方法转义为unicode,因此我使用了以下内容:

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

答案 4 :(得分:9)

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

任何JSON解释器都可以使用该字符串并重现原始字符串。要查看此操作,只需在浏览器的位置栏中输入javascript:alert("ok\u00e9")即可。

答案 5 :(得分:2)

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

答案 6 :(得分:0)

我有一个非常棘手的方法来解决这个问题。好吧,如果to_json不允许你拥有正确的代码,那么你可以直接尝试写:

render text: tags

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