Search code examples
pythonyamlruamel.yaml

Cannot write custom dictionaries to YAML


I have working python code that makes use of three nested classes:

@dataclass
class PanelGroup:
    num_panels: int
    panel_efficiency: float
    panel_surface: float
    elevation: int


class PanelString(dict[str, PanelGroup]):
    pass


class SolarInstallation(dict[str, PanelString]):
    pass

The way I create instances of these classes, statically, from my code is:

solar_installation = SolarInstallation(
        SSW_12=PanelString(G1=PanelGroup(4, 0.191, 1.705, 50),
                           G2=PanelGroup(5, 0.191, 1.705, 50)))

I want to make this configuration come from a YAML file, so I trying to get to work the following snippet of code to dump the correct YAML contents to a file:

yaml = ruamel.yaml.YAML()
yaml.register_class(SolarInstallation)
yaml.register_class(PanelString)
yaml.register_class(PanelGroup)
yaml.dump(solar_installation, sys.stdout)

But the only thing I get is an empty dictionary:

!SolarInstallation {}

What am I doing wrong?


Solution

  • You'll need to tell ruamel.yaml how to dump your classes, just registering is not enough:

    import sys
    from dataclasses import dataclass
    import ruamel.yaml
    
    
    @dataclass
    class PanelGroup:
        num_panels: int
        panel_efficiency: float
        panel_surface: float
        elevation: int
    
        @classmethod
        def to_yaml(cls, representer, node):
            d = {}
            for k in node.__annotations__:
                d[k] = node.__getattribute__(k)
            return representer.represent_mapping('!' + cls.__name__, d)
    
    class PanelString(dict[str, PanelGroup]):
        @classmethod
        def to_yaml(cls, representer, node):
            return representer.represent_mapping('!' + cls.__name__, node)
    
    
    class SolarInstallation(dict[str, PanelString]):
        @classmethod
        def to_yaml(cls, representer, node):
            return representer.represent_mapping('!' + cls.__name__, node)
    
    solar_installation = SolarInstallation(
            SSW_12=PanelString(G1=PanelGroup(4, 0.191, 1.705, 50),
                               G2=PanelGroup(5, 0.191, 1.705, 50)))
    yaml = ruamel.yaml.YAML()
    yaml.register_class(SolarInstallation)
    yaml.register_class(PanelString)
    yaml.register_class(PanelGroup)
    yaml.dump(solar_installation, sys.stdout)
    

    which gives:

    !SolarInstallation
    SSW_12: !PanelString
      G1: !PanelGroup
        num_panels: 4
        panel_efficiency: 0.191
        panel_surface: 1.705
        elevation: 50
      G2: !PanelGroup
        num_panels: 5
        panel_efficiency: 0.191
        panel_surface: 1.705
        elevation: 50