【问题标题】:How to read ruby object in YAML如何在 YAML 中读取 ruby​​ 对象
【发布时间】:2018-03-12 12:01:48
【问题描述】:

我有一个如下所示的 YAML 文件:

--- !ruby/object:Hi
num: 1
--- !ruby/object:Hi
num: 2

还有我的红宝石代码:

require 'yaml'
class A
 attr_accessor :num
 def initialize num
  @num=num
 end
end
a=A.new 1
b=A.new 2
File.open 'test.yml', 'r+' do |f|
 f.write YAML.dump a
 f.write YAML.dump b
 f.seek(0)
 #c=obj a
 #d=ojb b
end

我想让 p c 和 p d 输出如下:

#<A:0x00000 @num=1>
#<A:0x00001 @num=2>

【问题讨论】:

  • 你有没有试过把这个喂给YAML.parse或类似的?
  • 你那里的代码,它正在生成无效的 YAML。您不能只在一个文件中转储多个文档。
  • @SergioTulentsev 你绝对可以。 That's what the --- directive is for.

标签: ruby yaml


【解决方案1】:

我不知道为什么某些人如此坚持不能将多个 YAML 文档放在一个文件中,因为 YAML 格式是 explicitly designed to accommodate that particular use case。在 Ruby 中,您可以使用 YAML.load_stream 解析多个文档,这会将反序列化的对象作为数组返回:

require "yaml"

class A
  attr_accessor :num
  def initialize num
    @num = num
  end
end

a = A.new 1
b = A.new 2

File.open "test.yml", "r+" do |f|
  f.write YAML.dump(a)
  f.write YAML.dump(b)
  f.seek(0)

  c, d = YAML.load_stream(f)

  p c
  # => #<A:0x000055d423387cd0 @num=1>
  p d
  # => #<A:0x000055d423387528 @num=2>
end

您也可以将一个块传递给YAML.load_stream,在这种情况下,每个文档都会调用它:

YAML.load_stream(f) do |obj|
  p obj.num
end
# => 1
# => 2

您可以在 repl.it 上看到这两种情况:https://repl.it/@jrunning/FavorableElderlyAddition

【讨论】:

  • 这是一个很好的答案,我以前不知道这是可能的,非常感谢。这个答案也适用于偏执者:)
  • 我的立场是正确的。 :) 另外,我了解了load_stream。小时/吨。
  • 优秀。我花了一段时间才弄清楚如何从一个 YAML 文件中加载多个对象。官方的 ruby​​ 文档在那里有点薄。
【解决方案2】:
object = JSON.parse(YAML::load_file("./test.yaml").to_json, object_class: OpenStruct)

不是 100% 确定这些会以什么格式出现,但它会给你一个很好的起点

【讨论】:

  • JSON 在这里做什么?
  • 它应该把它变成一个JSON,OpenStruct可以从中创建一个对象吗?答案来自:stackoverflow.com/questions/28521571/…
  • 这个答案在这里不适用,因为问题不同。仔细查看输入文件,您可能会发现一个关键的区别。
  • @Mark,另外,如果您从其他地方复制代码,请添加一些归属。这是您在使用 StackOverflow 代码时必须应用的少数要求之一。见stackoverflow.blog/2009/06/25/attribution-required
【解决方案3】:

我更喜欢将对象写入单独的文件,但您的序列化方式也可以。 这是您的情况的解决方案;

RUBY_OBJECT = '--- !ruby/object:'

def load_objects(file_content)
  object_contents = file_content.split(RUBY_OBJECT)

  object_contents.map do |object_content|
    YAML.load(RUBY_OBJECT + object_content)
  end.compact
end

##########

File.open 'test.yml', 'r+' do |f|
  f.write YAML.dump(a)
  f.write YAML.dump(b)

  f.seek(0)
  file_content = f.read
  c, d = *load_objects(file_content)

  puts c
  puts d
end

您还可以对文件内容运行正则表达式以获取单个对象定义。

如果使用一个文件进行序列化是一项严格要求,您可以像这样将一组对象序列化到文件中;

require 'yaml'

class A
  attr_accessor :num

  def initialize(num)
    @num = num
  end
end

a = A.new(1)
b = A.new(2)

File.open('test.yml', 'w+') do |f|
  f.write YAML.dump([a, b])
end

objects = YAML.load_file('test.yml')

【讨论】:

  • “但是你的序列化方式也可以” - 不,不,不,这不行。 好的是生成一个有效的 yaml 文件(就像在你的第二个 sn-p 中一样)。那么你就不需要用额外的字符串修饰来补充YAML.load(这充其量是脆弱的)
  • 你永远不知道他的要求是什么。您可以将YAML.dump 的输出写入一个不应该是有效 YAML 文件的文件,最后它是您自己的序列化模式。您是否希望每次都将数百万个对象加载到内存中,以便能够序列化数组以生成有效的 yaml 文件?这就是为什么人们说红宝石很慢并且消耗大量内存的原因。 当然,将对象序列化到单独的文件中是更合适的方式,但并非总是!为了能够说“不,不,不行”,您必须了解要求。
  • 我看错了。它一个有效的 yaml 文件。你活着,你学习¯\_(ツ)_/¯ 但如果它不是,那么我坚持我的观点,写一个损坏的格式永远不是答案。除非您故意这样做,以与同样损坏的系统进行交互。但是解析这个是系统的任务。
  • “但如果不是,那么我坚持我的观点,编写损坏的格式永远不是答案。除非您故意这样做,以便与同样损坏的系统交互”。您已经知道有些案例仍然在说“从不”。你唯一能说从不的时候是,从不说从不;)
猜你喜欢
  • 1970-01-01
  • 2015-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
  • 2011-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多