Top level lists and dictionaries with marshmallow

Viewed 427

Is it possible to define marshmallow schema for top-level lists (e.g. obj = ['John', 'Bill', 'Jack']) or dictionaries (e.g. obj = {'user1': 123, 'user2': 456, 'user3': 789} - keys are arbitrary)?

2 Answers

Top-level dict is the normal case. To accept arbitrary keys, see unknown=INCLUDE, but then you have no validation, unlike when using the Dict field.

A solution to this would be either to define a default Field for unknown fields, or to extend Field to let it act as Schema. The former was already suggested (by me, I belive) and shouldn't be too hard to achieve but no one has taken the time to work on it. The latter was suggested by Jared and would be an important refactor. No one is working on it.

Top-level list of dicts is the normal case of using a schema with many=True. Top-level list of anything is not achievable yet. This would work with the refactor mentioned above.

As Jérôme said, a list of schemas is supported with the argument many=True.

For a dict with unknown number of keys (but possibly a known key/value type) everything is pretty much ready in the Dict field. Making a little subclass can get one the feeling of dealing with a normal Schema (at least w.r.t. dump and load), e.g.:

import typing
from marshmallow import fields
from marshmallow_dataclass import dataclass


class PoorMansDictSchema(fields.Dict):
    @staticmethod
    def _get_obj(obj, _attr, _default):
        return obj

    def dump(self, obj: typing.Any):
        return self.serialize('', obj, accessor=self._get_obj)

    def load(self, data: typing.Mapping[str, typing.Any]):
        return self.deserialize(data)


@dataclass
class Record:
    name: str
    value: float


dict_schema = PoorMansDictSchema(keys=fields.Str(),
                                 values=fields.Nested(Record.Schema))

records = {'a': Record('a', 1.0), 'b': Record('b', 2.0)}
print(records)
# {'a': Record(name='a', value=1.0), 'b': Record(name='b', value=2.0)}
serialized_records = dict_schema.dump(records)
print(serialized_records)
# {'a': {'name': 'a', 'value': 1.0}, 'b': {'name': 'b', 'value': 2.0}}
records_back = dict_schema.load(serialized_records)
print(records_back)
# {'a': Record(name='a', value=1.0), 'b': Record(name='b', value=2.0)}

I used the marshmallow-dataclass in the example to make it more concise (inferring the Schema from the class). Obviously, the PoorMansDictSchema's dump/load don't support the many, partial and unknown overrides among other Schema related perks, but those can still be defined in the nested schema.

Related