Search code examples
pythonpython-2.7yamlpyyaml

Python parsing class from YAML


I am trying to output and then to parse back from YAML the following

import numpy as np
class MyClass(object):
    YAMLTag = '!MyClass'

    def __init__(self, name, times, zeros):
        self.name   = name
        self._T     = np.array(times)
        self._zeros = np.array(zeros)

The YAML file looks like

!MyClass:
  name: InstanceId
  times: [0.0, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0]
  zeros: [0.03, 0.03, 0.04, 0.03, 0.03, 0.02, 0.03]

To write, I have added to the class two methods

def toDict(self):
    return {'name'  : self.name,
            'times' : [float(t) for t in self._T],
            'zeros' : [float(t) for t in self._zeros]}
@staticmethod
def ToYAML(dumper, data):
    return dumper.represent_dict({data.YAMLTag : data.toDict()})

and to read, the method

@staticmethod
def FromYAML(loader, node):
    nodeMap = loader.construct_mapping(node)
    return MyClass(name  = nodeMap['name'],
                   times = nodeMap['times'],
                   zeros = nodeMap['zeros'])

and following YAML Documentation, I added the following snippet in the same Python file myClass.py:

import yaml

yaml.add_constructor(MyClass.YAMLTag, MyClass.FromYAML)
yaml.add_representer(MyClass,         MyClass.ToYAML)

Now, the writing seems to work ok, but reading the YAML, the code

loader.construct_mapping(node)

seems to return the dictionary with empty data:

{'zeros': [], 'name': 'InstanceId', 'times': []}

How should I fix the reader to be able to do this properly? Or perhaps I am not writing something out right? I spent a long time looking at PyYAML documentation and debugging through how the package is implemented but cannot figure out a way to parse out a complicated structure, and the only example I seemed to find has a 1-line class which parses out easily.


Related: YAML parsing and Python


UPDATE

Manually parsing the node as follows worked:

name, times, zeros = None, None, None
for key, value in node.value:
    elementName = loader.construct_scalar(key)
    if elementName == 'name':
        name = loader.construct_scalar(value)
    elif elementName == 'times':
        times = loader.construct_sequence(value)
    elif elementName == 'zeros':
        zeros = loader.construct_sequence(value)
    else:
        raise ValueError('Unexpected YAML key %s' % elementName)

But the question still stands, is there a non-manual way to do this?


Solution

  • Instead of

    nodeMap = loader.construct_mapping(node)
    

    try this:

    nodeMap = loader.construct_mapping(node, deep=True)
    

    Also, you have a little mistake in your YAML file:

    !MyClass:
    

    The colon at the end does not belong there.