Проблема Ruby to_json с ошибкой "недопустимый / неправильно сформированный utf-8"

Я получил ошибку JSON::GeneratorError: source sequence is illegal/malformed utf-8 при попытке конвертировать хеш в строку json. Мне интересно, имеет ли это какое-либо отношение к кодированию, и как я могу заставить to_json просто обрабатывать \xAE как есть?

$ irb
2.0.0-p247 :001 > require 'json'
=> true
2.0.0-p247 :002 > a = {"description"=> "iPhone\xAE"}
=> {"description"=>"iPhone\xAE"}
2.0.0-p247 :003 > a.to_json
JSON::GeneratorError: source sequence is illegal/malformed utf-8
  from (irb):3:in `to_json'
  from (irb):3
  from /Users/cchen21/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'

2 ответа

Решение

\xAE не является допустимым символом в UTF-8, вы должны использовать \u00AE вместо:

"iPhone\u00AE"
#=> "iPhone®"

Или преобразовать его соответственно:

"iPhone\xAE".force_encoding("ISO-8859-1").encode("UTF-8")
#=> "iPhone®"

Каждая строка в Ruby имеет нижележащую кодировку. В зависимости от вашего LANG а также LC_ALL Переменные окружения, интерактивная оболочка может выполнять и интерпретировать ваши строки в заданной кодировке.

$ irb
1.9.3p392 :008 > __ENCODING__
 => #<Encoding:UTF-8>

(игнорируйте, что я использую Ruby 1.9 вместо 2.0, идеи все те же).

__ENCODING__ возвращает текущую исходную кодировку. Ваш, вероятно, также скажет UTF-8.

Когда вы создаете буквенные строки и используете экранирование байтов (\xAE) в вашем коде Ruby пытается интерпретировать это в соответствии с кодировкой строки:

1.9.3p392 :003 > a = {"description" => "iPhone\xAE"}
 => {"description"=>"iPhone\xAE"}
1.9.3p392 :004 > a["description"].encoding
 => #<Encoding:UTF-8>

Итак, байт \xAE в конце ваша литеральная строка будет рассматриваться как потоковый байт UTF-8, но она недопустима. Посмотрите, что происходит, когда я пытаюсь напечатать это:

1.9.3-p392 :001 > puts "iPhone\xAE"
iPhone�
 => nil

Вам либо нужно предоставить зарегистрированный символ метки в допустимой кодировке UTF-8 (либо используя реальный символ, либо предоставив два байта UTF-8):

1.9.3-p392 :002 > a = {"description1" => "iPhone®", "description2" => "iPhone\xc2\xae"}
 => {"description1"=>"iPhone®", "description2"=>"iPhone®"}
1.9.3-p392 :005 > a.to_json
 => "{\"description1\":\"iPhone®\",\"description2\":\"iPhone®\"}"

Или, если вы вводите ISO-8859-1 (Latin 1) и вы точно знаете это, вы можете указать Ruby интерпретировать вашу строку как другую кодировку:

1.9.3-p392 :006 > a = {"description1" => "iPhone\xAE".force_encoding('ISO-8859-1') }
 => {"description1"=>"iPhone\xAE"}
1.9.3-p392 :007 > a.to_json
 => "{\"description1\":\"iPhone®\"}"

Надеюсь, поможет.

Другие вопросы по тегам