#ruby #serialization #yaml
#ruby #сериализация #yaml
Вопрос:
В Ruby любой объект может быть перенесен, то есть сериализован, в документ YAML путем сохранения выходных данных метода « to_yaml
» в файл. После этого этот файл YAML можно прочитать снова, т. е. десериализовать, с помощью YAML::load
метода. Более того, у каждого есть полный доступ ко всем членам базового класса / объекта.
Все это справедливо, пока я использую Ruby как единую платформу. Как только я сериализую объекты в Java и десериализую их в Ruby, я больше не могу получить доступ к объекту из-за NoMethodError
исключения. Это связано с тем, как объекты / локальные типы данных называются в разных системах.
Учитывая класс Ruby «Car»:
# A simple class describing a car
#
class Car
attr :brand, :horsepower, :color, :extra_equipment
def initialize(brand, horsepower, color, extra_equipment)
@brand = brand
@horsepower = horsepower
@color = color
@extra_equipment = extra_equipment
end
end
Создание простого экземпляра:
# creating new instance of class 'Car' ...
porsche = Car.new("Porsche", 180, "red", ["sun roof", "air conditioning"])
Вызов porsche.to_yaml
приводит к следующему выводу:
--- !ruby/object:Car
brand: Porsche
color: red
extra_equipment:
- sun roof
- air conditioning
horsepower: 180
Я тестирую десериализацию, загружая выходные данные YAML:
# reading existing yaml file from file system
sample_car = YAML::load(File.open("sample.yaml"))
puts sample_car.brand # returns "Porsche"
Это работает, как ожидалось, но теперь давайте предположим, что документ YAML был создан другой системой и в нем отсутствуют какие-либо ссылки на Ruby, хотя в описании объекта, соответствующем yaml, « !Car
«, вместо « !ruby/object:Car
«:
--- !Car
brand: Porsche
color: red
extra_equipment:
- sun roof
- air conditioning
horsepower: 180
Этот код:
# reading existing yaml file from file system
sample_car = YAML::load(File.open("sample.yaml"))
puts sample_car.brand # returns "Porsche"
возвращает это исключение:
/path/yaml_to_object_converter.rb.rb:27:in `<main>':
undefined method `brand' for #<YAML::DomainType:0x9752bec> (NoMethodError)
Есть ли способ работать с объектами, определенными во «внешних» документах YAML?
Комментарии:
1. возможно, это поможет: blog.bytemine.net/2009/12/07 /…
2. Является ли «члены класса» (из названия) термином Java?
3. @Andrew: Я признаю, что это скорее терминология Java и, следовательно, немного неточно в данном контексте Ruby. Я имел в виду переменные класса и методы класса.
4. Я не вижу здесь никаких переменных класса или методов класса — только переменные экземпляра и методы экземпляра.
Ответ №1:
Для меня sample_car
в оболочке IRB вычисляется как:
=> #<Syck::DomainType:0x234df80 @domain="yaml.org,2002", @type_id="Car", @value={"brand"=>"Porsche", "color"=>"red", "extra_equipment"=>["sun roof", "air conditioning"], "horsepower"=>180}>
Затем я выдал sample_car.value
:
=> {"brand"=>"Porsche", "color"=>"red", "extra_equipment"=>["sun roof", "air conditioning"], "horsepower"=>180}
Который является хэшем. Это означает, что вы можете создать свой объект Car, добавив метод класса к Car
вот так:
def self.from_hash(h)
Car.new(h["brand"], h["horsepower"], h["color"], h["extra_equipment"])
end
Затем я попробовал это:
porsche_clone = Car.from_hash(sample_car.value)
Который вернул:
=> #<Car:0x236eef0 @brand="Porsche", @horsepower=180, @color="red", @extra_equipment=["sun roof", "air conditioning"]>
Это самый уродливый способ сделать это. Могут быть и другие. =)
РЕДАКТИРОВАТЬ (19 мая 2011): Кстати, просто придумал намного более простой способ:
def from_hash(o,h)
h.each { |k,v|
o.send((k "=").to_sym, v)
}
o
end
Чтобы это сработало в вашем случае, вашему конструктору не должны требоваться параметры. Тогда вы можете просто сделать:
foreign_car = from_hash(Car.new, YAML::load(File.open("foreign_car.yaml")).value)
puts foreign_car.inspect
…что дает вам:
#<Car:0x2394b70 @brand="Porsche", @color="red", @extra_equipment=["sun roof", "air conditioning"], @horsepower=180>
Комментарии:
1. Я действительно не считаю это уродливым. Я считаю, что это защитное программирование сводит любые объекты к их базовым типам и отправляет их. Это глазурь на торте YAML, которая пытается помочь нам, отправляя объекты, но эта функция также мешает. Перейдите к базовым значениям, и данные должны перемещаться без проблем.
2. Ну, я сказал это только потому, что уверен, что есть драгоценный камень, который может автоматизировать процесс еще больше, без необходимости разработчику добавлять методы класса. 😉 В остальном метод аккуратный.
3. Да, gem оптимизирует код, который его вызывает, но я думаю, что это просто намазывает торт большим количеством глазури, чтобы скрыть тот факт, что торт комом. 🙂 Хотя я согласен с вами, повторное использование gem — хороший способ добиться этого. Я бы, вероятно, добавил
to_yaml
метод к рассматриваемым классам, но это один из многих недостатков моего мозга.4. Я также согласен с тем, что программист должен обладать знаниями о классе (здесь «Car») и его свойствах. Затем он может реализовать метод «from_hash» и «реконструировать» объект. Но, насколько я могу судить, простого готового метода, позволяющего читать документы yaml, описывающие объекты из систем, отличных от Ruby, не существует. Особенно сильно вложенные объекты потребовали бы много времени.
5. Например, рассмотрим: — ! Автомобильный движок: !Клапаны geneclass по умолчанию: 6 допуск: !!float ‘1.39154E13’ кривошип: !DefaultCrankClass ширина: 100 высота: 200 материал: !DefaultMaterialClass тип: сталь производитель: West Coast Car Parts