Codifica JSON erroneamente sfuggita (Rails 3, Ruby 1.9.2)

Nel mio controller, i seguenti lavori (stampa “oké”)

puts obj.inspect 

Ma questo non funziona (rende “ok \ u00e9”)

 render :json => obj 

Apparentemente il metodo to_json sfugge ai caratteri unicode. C’è un’opzione per prevenire questo?

Se riesci a scavare attraverso il codice sorgente, arriveresti su ActiveSupport::JSON::Encoding e sul metodo di 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 

Le varie chiamate di gsub stanno forzando l’UTF-8 non ASCII alla notazione \uXXXX che stai vedendo. L’UTF-8 con codifica esadecimale dovrebbe essere accettabile per qualsiasi cosa che elabora JSON, ma è sempre ansible post-processare il JSON (o patch di scimmia in un escaper modificato JSON) per convertire la notazione \uXXXX in UTF-8 grezzo, se necessario.

Sono d’accordo sul fatto che forzare JSON a essere 7bit-clean è un po ‘falso ma ci vai.

Risposta breve: no.

Per reimpostare i codici \ uXXXX su utf-8:

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

Puoi prevenirlo con la patch della scimmia, il metodo menzionato da muu è troppo corto. Mettere quanto segue in config / initializers / patches.rb (o file simile usato per l’applicazione di patch) e riavviare il processo di rotaia affinché la modifica abbia effetto.

 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 

Sappi che non c’è alcuna garanzia che la patch funzioni con le versioni future di ActiveSupport. La versione utilizzata quando si scrive questo post è 3.1.3.

Questa è la codifica corretta. JSON non richiede l’escape dei caratteri Unicode, ma è comune per le librerie JSON produrre output che contiene solo caratteri ASCII a 7 bit, per evitare potenziali problemi di codifica durante il transito.

Qualsiasi interprete JSON sarà in grado di consumare quella stringa e riprodurre l’originale. Per vedere questo in azione, digita javascript:alert("ok\u00e9") nella barra degli indirizzi del browser.

I personaggi non sono stati sfuggiti Rails2.3.11/Ruby1.8 con gli altri metodi in Rails2.3.11/Ruby1.8 quindi ho usato quanto segue:

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

render: json chiamerà .to_json sull’object se non è una stringa. Puoi evitare questo problema facendo:

 render :json => JSON.generate(obj) 

Questo passerà direttamente una stringa e quindi eviterà la chiamata a to_json di ActiveSupport.

Un altro approccio sarebbe quello di sovrascrivere to_json sull’object che si sta serializzando, quindi in tal caso, si potrebbe fare qualcosa del tipo:

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

E se usi ActiveModelSerializers, puoi risolvere questo problema sovrascrivendo to_json nel tuo serializzatore:

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

Ho un modo molto complicato per risolvere questo problema. Bene, se to_json non ti permettesse di avere il codice corretto, allora potresti provare direttamente a scrivere:

 render text: tags 

render json: tags or render json: tags.to_json trasferirà sempre automaticamente lo stile di codifica, ma se usi il render text:tags , la stringa rimarrà così com’è. E penso che jQuery possa ancora riconoscere i dati.