Marshmallow: Clarification on using fields.Dict()

Created on 13 Apr 2016  Â·  5Comments  Â·  Source: marshmallow-code/marshmallow

Suppose I want to serialize a dict with complex objects, each with their own Schema, like:

class Inner():
    a = "testing"
    b = "123"
class InnerSchema(Schema):
    a = fields.Str()
    b = fields.Str()

class Outer():
    c = {}

Then populate it, like

o = Outer()
o.c['one'] = Inner()
o.c['two'] = Inner()

What I'd like the serialization to look like is something like:

{ 
"c": {
    "one": {"a": "testing", "b": "123"},
    "two": {"a": "testing", "b": "123"}
    }
}

What is the schema for class Outer? This gives an error trying to serialize the Inner objects:

class OuterSchema(Schema):
    c = fields.Dict(fields.Nested(InnerSchema, many=True))

or, also the same error:

class OuterSchema(Schema):
    c = fields.Dict(fields.Nested(InnerSchema),  many=True)

Is this possible with fields.Dict in Marshmallow? I have seen things like #424, #120, #258, it seems like it is possible with the new fields.Dict, but here it looks like the Nested is not propagating the Inner schema to the serializer.

TypeError: <__main__.Inner object at 0x7f9673300be0> is not JSON serializable

Thanks!

Most helpful comment

fields.Dict does not take an inner field as an argument; it does not apply any special formatting on serialization.

However, it would be easy enough to implement:

class ComposableDict(fields.Dict):

    def __init__(self, inner, *args, **kwargs):
        self.inner = inner
        super().__init__(*args, **kwargs)

    def _serialize(self, value, attr, obj):
        return {
            key: self.inner._serialize(val, key, value)
            for key, val in value.items()
        }

Then use it like so:

class Inner():
    a = "testing"
    b = "123"

class Outer():
    c = {}

class InnerSchema(Schema):
    a = fields.Str()
    b = fields.Str()

class OuterSchema(Schema):
    c = ComposableDict(fields.Nested(InnerSchema))

o = Outer()
o.c['one'] = Inner()
o.c['two'] = Inner()

sch = OuterSchema()
print(sch.dump(o).data)
# {'c': {'one': {'a': 'testing', 'b': '123'}, 'two': {'a': 'testing', 'b': '123'}}}

All 5 comments

fields.Dict does not take an inner field as an argument; it does not apply any special formatting on serialization.

However, it would be easy enough to implement:

class ComposableDict(fields.Dict):

    def __init__(self, inner, *args, **kwargs):
        self.inner = inner
        super().__init__(*args, **kwargs)

    def _serialize(self, value, attr, obj):
        return {
            key: self.inner._serialize(val, key, value)
            for key, val in value.items()
        }

Then use it like so:

class Inner():
    a = "testing"
    b = "123"

class Outer():
    c = {}

class InnerSchema(Schema):
    a = fields.Str()
    b = fields.Str()

class OuterSchema(Schema):
    c = ComposableDict(fields.Nested(InnerSchema))

o = Outer()
o.c['one'] = Inner()
o.c['two'] = Inner()

sch = OuterSchema()
print(sch.dump(o).data)
# {'c': {'one': {'a': 'testing', 'b': '123'}, 'two': {'a': 'testing', 'b': '123'}}}

Thanks! This works perfectly!

Still useful.
In python 2.7: super(fields.Dict, self).__init__(*args, **kwargs)

Note that in Marshmallow 3 (still alpha version as of today), it is possible to specify nested fields for dict keys and values: http://marshmallow.readthedocs.io/en/latest/api_reference.html#marshmallow.fields.Dict.

Right. I just test new implementation from https://github.com/marshmallow-code/apispec/issues/201 and it is much better.

Test with 3.0.0b11 + apispec 0.38

Was this page helpful?
0 / 5 - 0 ratings