从 yaml 扩展哈希和(反)序列化

发布于 2024-12-03 20:58:12 字数 287 浏览 1 评论 0原文

我找到了这个例子: http://www.yaml.org/YAML_for_ruby.html#extending_kernel::hash

很高兴看到 YAML 如何轻松地序列化哈希。 但是,我想序列化我自己的类,一个带有属性的扩展哈希。

yaml 字符串是什么样子以及序列化它的最佳方法是什么?

提前致谢

I found this example:
http://www.yaml.org/YAML_for_ruby.html#extending_kernel::hash

It's nice to see how easy YAML can serialize a hash.
However, I would like to serialize my own class, an extended hash WITH the attribute in it.

What would the yaml-string look like and what would be the best way to serialize it?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

墨离汐 2024-12-10 20:58:12

您可以获取原始的来源 Hash#to_yaml 并覆盖它,添加您需要的任何内容:

def to_yaml( opts = {} )
  YAML::quick_emit( self, opts ) do |out|
    out.map( taguri, to_yaml_style ) do |map|
      # here you can add something like:
      map.add('my_attribute', @my_attribute)
      # and proceed with the usual Hash serialization:
      each do |k, v|
        map.add( k, v )
      end
    end
  end
end

You can take the source of original Hash#to_yaml and override it, adding whatever you need:

def to_yaml( opts = {} )
  YAML::quick_emit( self, opts ) do |out|
    out.map( taguri, to_yaml_style ) do |map|
      # here you can add something like:
      map.add('my_attribute', @my_attribute)
      # and proceed with the usual Hash serialization:
      each do |k, v|
        map.add( k, v )
      end
    end
  end
end
一袭白衣梦中忆 2024-12-10 20:58:12

正如 Mladen Jablanović 的回答所示,您可以覆盖 to_yaml。您可以添加一个名为“attributes”的数组(如果哈希中存在具有该名称的键,请特别注意转义该名称(如果...等,请注意转义转义的名称))。然而,您需要一些内部知识才能完成这项工作(out.map(tag_uri, to_yaml_style) 及其变体非常重要,并且没有很好的文档记录:各种 Ruby 解释器的源代码是您最好的选择赌注)。

不幸的是,您还需要覆盖反序列化过程。如何重用现有代码几乎完全没有文档记录。正如 此答案 中所示,您会发现需要添加 to_yaml_type 并使用 YAML::add_domain_type 添加反序列化代码。从那里开始,您几乎要靠自己了:您需要编写半个 YAML 解析器来解析 yamled 字符串并将其转换为您的对象。

可以解决这个问题,但是更简单的解决方案(我上次想要这个时实现的)是使哈希成为我的对象的属性,而不是扩展哈希。后来我意识到我实际上并没有实现 Hash 的子类。存储键值对的东西并不一定意味着它是哈希。如果您实现 :[]:[]=each,您通常会在能够将对象视为好像它是一个哈希值。

As Mladen Jablanović's answer shows, you can override to_yaml. You could add an array named 'attributes' (taking special care to escape that name if there is a key in the hash with that name (taking care to escape the escaped name if ... etc.)). However, you need some knowledge of the internals to make this work (the out.map(tag_uri, to_yaml_style) and its variations are nontrivial and not well documented: the sources of the various Ruby interpreters are your best bet).

Unfortunately, you also need to override the deserialization process. How you can reuse existing code there is close to completely undocumented. As in this answer, you see you would need to add a to_yaml_type and add the deserialization code using YAML::add_domain_type. From there, you are pretty much on your own: you need to write half a YAML parser to parse the yamled string and convert it into your object.

It's possible to figure it out, but the easier solution, that I implemented last time I wanted this, was to just make the Hash an attribute of my object, instead of extending Hash. And later I realized I wasn't actually implementing a subclass of Hash anyway. That something is storing key-value pairs doesn't necessarily mean it is a Hash. If you implement :[], :[]= and each, you usually get a long way towards being able to treat an object as if it is a Hash.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文