Подтвердить что ты не робот

Кодирование JSON ошибочно экранировано (Rails 3, Ruby 1.9.2)

В моем контроллере следующие работы (отпечатки "oké" )

puts obj.inspect

Но это не делает (делает "ok\u00e9" )

render :json => obj

По-видимому, метод to_json избегает символов Unicode. Есть ли возможность предотвратить это?

4b9b3361

Ответ 1

Если вы копаете источник, вы в конце концов придете к 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, которое вы видите. Hex-кодированный UTF-8 должен быть приемлемым для всего, что обрабатывает JSON, но вы всегда можете отправлять JSON (или патч обезьяны в модифицированном escaper JSON), чтобы преобразовать нотацию \uXXXX в необработанный UTF-8, если это необходимо.

Я бы согласился с тем, что заставить JSON быть чистым 7 бит немного фиктивным, но там вы идете.

Короткий ответ: нет.

Ответ 2

Чтобы вернуть коды \uXXXX в utf-8:

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

Ответ 3

Вы можете предотвратить это путем обезглавливания метода, упомянутого muu, слишком коротким. Поместите следующее в config/initializers/patches.rb(или аналогичный файл, используемый для исправления материала) и перезапустите процесс рельсов, чтобы изменения повлияли.

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.

Ответ 4

Это правильная кодировка. JSON не требует, чтобы символы Unicode были экранированы, но для JSON-библиотек для вывода данных, содержащих только 7-битные символы ASCII, часто возникает проблема во избежание любых возможных проблем с кодированием в пути.

Любой интерпретатор JSON сможет использовать эту строку и воспроизвести оригинал. Чтобы увидеть это в действии, просто введите javascript:alert("ok\u00e9") в свою панель браузера.

Ответ 5

Символы не были экранированы в unicode с другими методами в Rails2.3.11/Ruby1.8, поэтому я использовал следующее:

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

Ответ 6

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

Ответ 7

У меня есть очень сложный способ решить эту проблему. Ну, если to_json не позволял вам иметь правильный код, вы могли бы попытаться написать:

render text: tags

render json: tags или render json: tags.to_json всегда будет автоматически передавать стиль кодировки, но если вы используете render text:tags, строка останется такой, какая есть. И я думаю, что jQuery все еще может распознавать данные.