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