stackit
stackit

Reputation: 3106

PYAML throwing a representation error

I have this nested dictionary class, whose instances I need to dump into YAML

class NestedDict(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

On dumping this dictionary:

pyaml.dump(nesteddict)

I get this error(only an excerpt of the entire message object posted):

"RepresenterError: cannot represent an object: {'a1401': 'ts755', 'ts64': {'topic': {'a1561': 'Process Control'}}, 'a1450': 'ts107', 'a1609': 'ts341', 'a1400': 'ts753', ......

So how to neatly represent this in YAML? I read that PyYAML does support nested recursive structures.

Upvotes: 3

Views: 2703

Answers (1)

Anthon
Anthon

Reputation: 76578

YAML does support nested recursive structures:

import ruamel.yaml

data = [1, 3]
data.append(data)

print ruamel.yaml.dump(data)

Will give you YAML with an anchor (&id001) and a reference (*id001):

&id001
- 1
- 3
- *id001

And your simple derivative of a dict can be dumped, at least with ruamel.yaml without a problem as well¹:

import ruamel.yaml

class NestedDict(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

data = NestedDict(a=1, b=2)
data['c'] = data

print ruamel.yaml.dump(data)

This gives you:

&id001 !!python/object/new:__main__.NestedDict
dictitems:
  a: 1
  b: 2
  c: *id001

If you need a more complex representation, e.g. filter out some dict values, you have to tell ruamel.yaml how to represent your objects. To tell it that NestedDict has a representer you can either make it a subclass of ruamel.yaml.YAMLObject, provide a class attribute yaml_tag = "!NestedDict" and implement __repr__(); or you can provide an explicit representer.

The latter is what is internally used and IMO somewhat easier to implement as you can just piggy-back on the dictionary code by having that return a dictionary representation (i.e top level key-value pairs) of your object.

def nested_dict_representer(dumper, node):
    return dumper.represent_mapping("!NestedDict", xyz)

Where xyz is some key-value pair representation of your object.


¹ Disclaimer: I am the author of that package, which is an enhanced version of PyYAML.

Upvotes: 2

Related Questions