如何从外部源反序列化YAML文档并对类成员具有完全访问权限?

时间:2011-05-11 14:36:10

标签: ruby serialization yaml

在Ruby中,通过将“to_yaml”方法的输出保存到文件,可以将任何对象传输(即序列化)到YAML文档。然后,可以使用YAML::load方法再次读取该YAML文件,即反序列化。此外,一个人可以完全访问底层类/对象的所有成员。

只要我将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”,而不是“{{ 1}}“:

!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"

有没有办法处理“外部”YAML文档中定义的对象?

1 个答案:

答案 0 :(得分:0)

对于我来说,IRB shell中的sample_car评估为:

=> #<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"]>

这是最丑陋的做法。可能还有其他人。 =)

编辑(2011年5月19日):顺便说一句,只是想方便了很多:

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>