1

我已经隔离了 Ruby on Rails 的一个问题,其中带有序列化列的模型无法正确加载已保存到其中的数据。

进去的是一个 Hash,出来的是一个 YAML 字符串,由于格式问题无法解析。我希望序列化程序可以正确存储和检索您提供的任何内容,因此似乎出现了问题。

有问题的麻烦字符串的格式如下:

message_text = <<END

  X
X
END

yaml = message_text.to_yaml

puts yaml
# =>
# --- |
#
#   X
# X

puts YAML.load(yaml)
# => ArgumentError: syntax error on line 3, col 0: ‘X’

换行、缩进的第二行和不缩进的第三行的组合会导致解析器失败。省略空行或缩进似乎可以解决问题,但这似乎是序列化过程中的一个错误。由于它需要一组相当独特的情况,我敢打赌这是一些没有得到妥善处理的奇怪边缘情况。

Ruby 附带并由 Rails 使用的 YAML 模块看起来将大部分处理委托给 Syck,但确实为 Syck 提供了一些关于如何编码它正在发送的数据的提示。

在 yaml/rubytypes.rb 中有 String#to_yaml 定义:

class String
  def to_yaml( opts = {} )
    YAML::quick_emit( is_complex_yaml? ? self : nil, opts ) do |out|
      if is_binary_data?
        out.scalar( "tag:yaml.org,2002:binary", [self].pack("m"), :literal )
      elsif to_yaml_properties.empty?
        out.scalar( taguri, self, self =~ /^:/ ? :quote2 : to_yaml_style )
      else
        out.map( taguri, to_yaml_style ) do |map|
          map.add( 'str', "#{self}" )
          to_yaml_properties.each do |m|
            map.add( m, instance_variable_get( m ) )
          end
        end
      end
    end
  end
end

似乎有一个检查以 ':' 开头的字符串,并且在反序列化时可能会被混淆为符号,并且 :quote2 选项应该指示在编码过程中引用它。调整此正则表达式以捕获上述条件似乎对输出没有任何影响,因此我希望更熟悉 YAML 实现的人可以提出建议。

4

2 回答 2

4

是的,这看起来像是 C syck 库中的一个错误。我使用 PHP syck 绑定(v 0.9.3)检查了它:http://pecl.php.net/package/syck并且存在相同的错误,表明它是库中的错误,而不是 ruby​​ yaml库或 ruby​​-syck 绑定:

// phptestsyck.php
<?php
$message_text = "

  X
X
";

syck_load(syck_dump($message_text));
?>

在 cli 上运行它会产生相同的 SyckException:

$ php phptestsyck.php 
PHP Fatal error:  Uncaught exception 'SyckException' with message 'syntax error on line 5, col 0: 'X'' in /.../phptestsyck.php:8
Stack trace:
#0 /.../phptestsyck.php(8): syck_load('--- %YAML:1.0 >...')
#1 {main}
  thrown in /.../phptestsyck.php on line 8

所以,我想你可以尝试修复 Syck 本身。不过,自 2005 年 5 月 ( http://rubyforge.org/projects/syck/ )的 v0.55 以来,该库似乎没有更新。

或者,有一个名为 RbYAML ( http://rbyaml.rubyforge.org/ ) 的纯 ruby​​ yaml 解析器,它起源于 JRuby,似乎没有这个错误:

>> require 'rbyaml'
=> true
>> message_text = <<END

  X
X
END
=> "\n  X\nX\n"
>> yaml = RbYAML.dump(message_text)
=> "--- "\\n  X\\nX\\n"\n"
>> RbYAML.load(yaml)
=> "\n  X\nX\n"
>> 

最后,您是否完全考虑过另一种序列化格式?Ruby 的 Marshal 库也没有这个错误,并且比 Yaml 更快(参见http://significantbits.wordpress.com/2008/01/29/yaml-vs-marshal-performance/):

>> message_text = <<END

  X
X
END
=> "\n  X\nX\n"
>> marshal = Marshal.dump(message_text)
=> "\004\b"\f\n  X\nX\n"
>> Marshal.load(marshal)
=> "\n  X\nX\n"
于 2009-11-18T17:46:34.623 回答
1

您必须放弃简单的serializeActiveRecord::Base 方法才能这样做,但使用您自己的序列化方案并不难。例如,要序列化一些名为“person_data”的字段:

class Person < ActiveRecord::Base
 def person_data
    self[:person_data] ? Marshal.load(self[:person_data]) : nil
  end

  def person_data=(x)
    self[:person_data] = Marshal.dump(x)
  end
end

## User Person#person_data as normal and it is transparently marshalled
p = Person.find 1
p.person_data = {:color => "blue", :food => "vegetarian"}

(有关更多信息,请参阅此ruby​​ 论坛主题

于 2009-11-19T18:24:56.653 回答