Len
Len

Reputation: 2145

Extending Hash and (de)serializing from/to 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

Upvotes: 1

Views: 1540

Answers (2)

Confusion
Confusion

Reputation: 16851

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.

Upvotes: 1

Mladen Jablanović
Mladen Jablanović

Reputation: 44090

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

Upvotes: 2

Related Questions