2009-10-21 4 views
1

Я изолировал проблему с Ruby on Rails, где модель с сериализованным столбцом неправильно загружает данные, которые были сохранены в ней.YAML Кодирование искаженной строки, проблемы сериализации модели

Что происходит, это хэш, и то, что выходит, это строка 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’ 

Сочетание новой строки с отступом второй линии, и без отступа третьей линии приводит к тому, анализатору потерпеть неудачу. Опуская либо пустую строку, либо отступ, похоже, устраняет проблему, но это, похоже, является ошибкой в ​​процессе сериализации. Поскольку для этого требуется довольно уникальный набор обстоятельств, я готов поспорить, что это какой-то странный крайний случай, который неправильно обрабатывается.

Модуль YAML, который поставляется с Ruby и используется Rails, считает, что делегировать большую часть обработки Syck, но предоставляет Syck некоторые подсказки относительно того, как кодировать передаваемые данные.

В YAML/rubytypes.rb есть струнное определение # 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

Да, это похоже на ошибку в библиотеке C syck. Я проверил это с помощью PHP-syck переплетов (V 0.9.3): http://pecl.php.net/package/syck и ту же ошибку, присутствует, показывая, что это ошибка в библиотеке, в отличие от библиотеки рубин YAML или рубин-syck привязок:

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

    X 
X 
"; 

syck_load(syck_dump($message_text)); 
?> 

Запуск этого на кли дает тот же 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. Похоже, что библиотека не обновлялась с v0.55 в мае 2005 года (http://rubyforge.org/projects/syck/).

С другой стороны, есть чисто-рубиновый YAML парсер называется RbYAML (http://rbyaml.rubyforge.org/), которая возникла с 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" 
>> 

Наконец, вы рассмотрели другой формат сериализации вообще? библиотека маршала Руби не имеет эту ошибку, либо и быстрее, чем 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" 
+0

Спасибо за понимание. Я до сих пор не знаю, с кем связаться, чтобы решить эту проблему, но есть проект Github (http://github.com/indeyets/syck/), который выполняется из старого репозитория _why. Я столкнулся с этой проблемой при сериализации вывода HTML HAML в столбец ActiveRecord. HAML, по совпадению, форматирует HTML с той же структурой с двумя пространствами, что и YAML, и запускает этот регистр. – tadman

+0

Считаете ли вы использование другой схемы сериализации, такой как маршал? Должно быть, это ямль? – bantic

+0

Что мне нужно - это то, что позволяет исправить поведение сериализованных столбцов ActiveRecord. Если есть способ использовать Маршала, я все для этого, но я не видел никакой информации о том, как это сделать без серьезной атаки. – tadman

1

Вы должны отказаться от простого метода serialize ActiveRecord :: 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 forum thread для более)

+0

Похоже, что это может извлечь выгоду из некоторого кеширования, но может работать. Благодаря! – tadman

+0

А, ну, это работает, только если вы на самом деле напишите в поле с чем-то замороженным. Например, назначив хэш, который вы позже измените, будет сохранено только начальное состояние. – tadman

 Смежные вопросы

  • Нет связанных вопросов^_^