Search code examples
jsonpython-3.xserializationmarshmallow

JSON serialization using Marshmallow - skip None attributes


I am using Marshmallow to send instance of my Decision class to JSON. However, this will also dump the attributes which are None, e.g. my attribute score will translate to null in JSON. After that I am unable to read the JSON again using the same approach.

https://repl.it/repls/VoluminousMulticoloredFacts

The last line is where it currently fails. I need to either NOT dump None to JSON or skip null during loading:

import json
from marshmallow import Schema, fields, post_load

json_data = """{
    "appid": "2309wfjwef",
    "strategy": "First Strategy"
}"""

# Output class definition
class Decision(object):
    def __init__(self, appid = None, strategy = None, score = None):
        self.appid = appid
        self.strategy = strategy
        self.score = score

class DecisionSchema(Schema):
    appid = fields.Str()
    strategy = fields.Str()
    score = fields.Int()

    @post_load
    def make_decision(self, data):
        return Decision(**data)

# Deserialization into object
dec_json = json.loads(json_data)
schema = DecisionSchema()
dec = schema.load(dec_json).data

print(dec.strategy)

# Dump results back to JSON
schema = DecisionSchema()
out = schema.dumps(dec)

print(out.data)

# Load back from dump
schema = DecisionSchema()
dec = schema.load(out).data

#print(dec.strategy) # returns error currently

Solution

  • An "official" answer from marshmallow development team can be found in this comment in the bugtracker:

    Use a post_dump method.

    from marshmallow import Schema, fields, post_dump
    
    class BaseSchema(Schema):
        SKIP_VALUES = set([None])
    
        @post_dump
        def remove_skip_values(self, data, **kwargs):
            return {
                key: value for key, value in data.items()
                if value not in self.SKIP_VALUES
            }
    
    
    class MySchema(BaseSchema):
        foo = fields.Field()
        bar = fields.Field()
    
    
    sch = MySchema()
    sch.dump({'foo': 42, 'bar': None}).data  # {'foo': 42}
    

    As I point out in a further comment, there's a shortcoming: it will also remove None when the field's allow_none is True.