ActiveRecord是否更改了序列化哈希的编码

时间:2016-05-20 12:15:59

标签: mysql ruby-on-rails ruby activerecord utf-8

我有一个Rails应用程序接受来自第三方来源的JSON数据,我相信我正在遇到一些ActiveRecord幕后魔术,它正在识别从JSON中提取的哈希中的ASCII-8BIT字符并保存它们无论我做什么,都是这样的。

以下是班级的简要描述......

class MyClass < ActiveRecord::Base
 serialize :data
end

以及如何创建对象......

a = MyClass.new 
a.data = {
  "a" =>
    {
      "b" => "bb",  
      "c" => "GIF89a\x01\x00\x01\x00\x00\x00\x00\x00!\vNETSCAPE2.0\x03\x01\x00\x00!\x04\t\x00\x00\x01\x00,\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02L\x01\x00;"
    }
}

我相信这些是ASCII-8BIT字符,如果它们被保存的话就足够公平(尽管我尝试过无处不在的UTF8)。但是我需要这些字符是UTF-8,因为当我去查看它们时,我得到:

ActionView::Template::Error ("\xEF" from ASCII-8BIT to UTF-8):
    64:       <div>
    65:         <pre><%= mc.prettify %></pre>
    66:       </div>
app/models/my_class.rb:28:in `prettify'

prettify中的第28行是:

JSON.pretty_generate(self.data)

所以我试图重新编码哈希中的任何字符串。我构建了执行此操作的功能(anonymous classesrefinementsHashArrayString),但无论如何,都会返回ASCII-8BIT对我来说。最简单的说法就是发生了什么:

mc = MyClass.find(123)
mc.data['a']['c'].encode!(Encoding.find('UTF-8'), {invalid: :replace, undef: :replace, replace: ''})
mc.data['a']['c'].encoding #=> #<Encoding:UTF-8>
mc.data['a']['c'].valid_encoding? #=> true
mc.save!
mc.reload
mc.data['a']['c'].encoding #=> #<Encoding:ASCII-8BIT> <-- !!!!!

ActiveRecord在保存时对此哈希做了什么?在serialize d MySQL(v5.6,通过mysql2 gem)mediumtext列(使用Ruby 2.2.4和Rails 4.1.4)中,我可以做什么来永久存储所有编码为UTF-8的字符串的哈希值)?

的my.cnf

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

[mysqld]
# ...
skip-character-set-client-handshake
collation_server=utf8_unicode_ci
init-connect='SET NAMES utf8mb4'
character-set-server=utf8

1 个答案:

答案 0 :(得分:2)

所以,实际上并不存在“ASCII-8BIT”字符。 ASCII-8BIT对ruby本质上意味着“根本不编码” - 只是字节,而不假设任何编码。它是'BINARY'的同义词。

但是如果你的字节不是有效的UTF-8,它们实际上不能编码为UTF-8。即使字符串上的编码是UTF-8,当您尝试对其执行某些操作时,最多会出现大量InvalidEncoding错误。

字符串最终标记为什么编码取决于ActiveRecord与数据库本身之间的复杂舞蹈 - 同样,数据库本身有时可能更改您的字节,具体取决于数据库和方式它的设置和你正在做的事情。我们可以尝试准确调试您正在做的事情。

但实际上,答案是 - 如果你想要它是UTF-8,它就不能有二进制非UTF8数据。 “ASCII-8BIT”实际上二进制数据的正确编码。你究竟想做什么,这些奇怪的字节来自哪里,为什么你想要它们?一般来说,我不确定在JSON中放置任意非UTF8字节是否合法?对于JSON来说它可能是合法的,但它可能会让你头疼(比如你正在处理的那个),因为它取决于两个rails和你的底层数据库究竟要用它们做什么。

为了解决您的显示错误,您可以使用prettify方法使用scrub,在ruby 2.1.0中添加以消除当前编码的错误字节。 value.force_encoding("UTF-8").scrub。这可能会解决你的错误,也许会做正确的事情,但最好弄清楚到底是什么,为什么你想要那些奇怪的字节,它们是什么意味着什么目的。