从YAML创建嵌套对象以通过Ruby中的方法调用访问属性

Har*_*dav 5 parsing yaml openstruct

我对红宝石完全陌生。我必须解析一个YAML文件来构造一个对象

YAML文件

projects:
  - name: Project1
    developers:
      - name: Dev1
        certifications:
          - name: cert1
      - name: Dev2
        certifications:
          - name: cert2
  - name: Project2
    developers:
      - name: Dev1
        certifications:
          - name: cert3
      - name: Dev2
        certifications:
          - name: cert4
Run Code Online (Sandbox Code Playgroud)

我想从此YAML创建一个对象,为此我在Ruby中编写了以下代码

require 'yaml'
object = YAML.load(File.read('./file.yaml'))
Run Code Online (Sandbox Code Playgroud)

我可以使用[]成功访问该对象的属性,例如

puts object[projects].first[developers].last[certifications].first[name]
# prints ABC
Run Code Online (Sandbox Code Playgroud)

但是,我想通过方法调用访问属性

例如

puts object.projects.first.developers.last.certifications.first.name
# should print ABC
Run Code Online (Sandbox Code Playgroud)

有什么方法可以构造可以通过上述(点)方式访问其属性的对象?我已经读过有关OpenStructhashugar的文章。我也想避免使用第三方宝石

pet*_*ter 6

Xavier 的回答很好,但它可以更短,只需要 yaml、json 和 ostruct 并解析您的 YAML,将其转换为 JSON,在 Openstruct 中解析它(也可以使用 Struct),就像这样

object = JSON.parse(YAML.load(yaml).to_json, object_class: OpenStruct)
Run Code Online (Sandbox Code Playgroud)

要从文件加载您的 YAML,它是

object = JSON.parse(YAML::load_file("./test.yaml").to_json, object_class: OpenStruct)
Run Code Online (Sandbox Code Playgroud)

这给

object
=>#<OpenStruct projects=[#<OpenStruct name="Project1", developers=[#<OpenStruct name="Dev1", certifications=[#<OpenStruct name="cert1">]>, #<OpenStruct name="Dev2", certifications=[#<OpenStruct name="cert2">]>]>, #<OpenStruct name="Project2", developers=[#<OpenStruct name="Dev1", certifications=[#<OpenStruct name="cert3">]>, #<OpenStruct name="Dev2", certifications=[#<OpenStruct name="cert4">]>]>]>
object.projects.first.developers.last.certifications.first.name 
=>cert2
Run Code Online (Sandbox Code Playgroud)

我用它来从文件加载配置,Yaml 很容易维护,并且在你的代码中它比 Hash 中的配置更容易使用。

不要为重复性任务执行此操作。


Xav*_*hay 5

如果您只是进行实验,可以使用一种快速而肮脏的方法来执行此操作:

class Hash
  def method_missing(name, *args)
    send(:[], name.to_s, *args)
  end
end
Run Code Online (Sandbox Code Playgroud)

不过,我不会在生产代码中使用它,因为method_missing猴子补丁和猴子补丁通常都是解决问题的良方。

更好的解决方案是递归遍历数据结构,并用openstruct代替哈希。

require 'ostruct'
def to_ostruct(object)
  case object
  when Hash
    OpenStruct.new(Hash[object.map {|k, v| [k, to_ostruct(v)] }])
  when Array
    object.map {|x| to_ostruct(x) }
  else
    object
  end
end

puts to_ostruct(object).projects.first.developers.last.certifications.first.name
Run Code Online (Sandbox Code Playgroud)

请注意,如果您经常这样做,则这两种方法都可能存在性能问题-如果您的应用程序对时间敏感,请确保对它们进行基准测试!不过,这可能与您无关。