serializers rails fast ruby-on-rails ruby json encoding

ruby on rails - fast - La codificación JSON escapó erróneamente(Rails 3, Ruby 1.9.2)



ruby encoding (7)

En mi controlador, los siguientes trabajos (imprime "oké")

puts obj.inspect

Pero esto no lo hace (hace que "ok / u00e9")

render :json => obj

Aparentemente el método to_json escapa de los caracteres Unicode. ¿Hay alguna opción para evitar esto?


Esa es la codificación correcta. JSON no requiere que se escapen los caracteres Unicode, pero es común que las bibliotecas JSON produzcan una salida que contenga solo caracteres ASCII de 7 bits, para evitar cualquier posible problema de codificación en tránsito.

Cualquier intérprete JSON podrá consumir esa cadena y reproducir el original. Para ver esto en acción, simplemente escriba javascript:alert("ok/u00e9") en la barra de ubicación de su navegador.


Los personajes no se escaparon a Unicode con los otros métodos en Rails2.3.11/Ruby1.8 así que usé lo siguiente:

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


Para volver a configurar los códigos / uXXXX en utf-8:

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


Puede evitarlo parcheando el mono, el método mencionado por muu es demasiado corto. Coloque lo siguiente en config / initializers / patches.rb (o un archivo similar utilizado para parchear cosas) y reinicie el proceso de rieles para que el cambio surta efecto.

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

Tenga en cuenta que no hay garantía de que el parche funcione con futuras versiones de ActiveSupport. La versión utilizada al escribir este post es 3.1.3.


Si ActiveSupport::JSON::Encoding la fuente, eventualmente llegará a ActiveSupport::JSON::Encoding y al método de 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

Las diversas llamadas a gsub están obligando a UTF-8 no ASCII a la notación /uXXXX que está viendo. El UTF-8 codificado en hexadecimal debería ser aceptable para cualquier cosa que procese JSON, pero siempre se puede postprocesar el JSON (o parche de mono en un Escaper JSON modificado) para convertir la notación /uXXXX a UTF-8 sin /uXXXX si es necesario.

Estoy de acuerdo en que obligar a JSON a limpiar con 7 bits es un poco falso, pero ya está.

Respuesta corta: no.


Tengo una manera muy difícil de resolver este problema. Bueno, si to_json no te permitiera tener el código correcto, entonces podrías intentar escribir directamente:

render text: tags

render json: tags o render json: tags.to_json siempre transferirá automáticamente el estilo de codificación, pero si usa render text:tags , la cadena permanecerá como está. Y creo que jQuery todavía podría reconocer los datos.


render: json llamará a .to_json en el objeto si no es una cadena. Puedes evitar este problema haciendo:

render :json => JSON.generate(obj)

Esto pasará una cadena directamente y, por lo tanto, evitará la llamada a to_json de ActiveSupport.

Otro enfoque sería anular to_json en el objeto que está serializando, por lo que en ese caso, podría hacer algo como:

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

Y si usa ActiveModelSerializers, puede resolver este problema anulando to_json en su serializador:

# controller respond_with foo, :serializer => MySerializer # serializer attributes :bar, :baz def to_json(options = {}) JSON.generate(serializable_hash) end