Сериализация JSON с использованием Marshmallow — отсутствие пропуска атрибутов

#json #python-3.x #serialization #marshmallow

#json #python-3.x #сериализация #marshmallow

Вопрос:

Я использую Marshmallow для отправки экземпляра моего класса Decision в JSON. Однако при этом также будут удалены атрибуты, которые являются None , например, мой атрибут score будет переведен в null в JSON. После этого я не могу снова прочитать JSON, используя тот же подход.

https://repl.it/repls/VoluminousMulticoloredFacts

Последняя строка — это то место, где в данный момент происходит сбой. Мне нужно либо НЕ выполнять сброс None в JSON, либо пропускать null во время загрузки:

 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
  

Ответ №1:

«Официальный» ответ от команды разработчиков marshmallow можно найти в этом комментарии к bugtracker:

Используйте post_dump метод.

 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}
  

Как я указываю в дальнейшем комментарии, есть недостаток: он также будет удален, None когда поле allow_none равно True .

Комментарии:

1. Еще одна проблема заключается в том, что это нарушит порядок ваших полей, если вы используете ordered = True

2. Конечно. Метод может быть изменен, чтобы сохранить порядок и вернуть OrderedDict .

3. Неплохо. Поскольку version post_load всегда передает аргумент many , поэтому метод должен выглядеть следующим образом def remove_skip_values(self, data, many):

4. Да, начиная с marshmallow 3, оформленные методы должны принимать ** kwargs.

Ответ №2:

Как я указал в своем комментарии выше, это нарушает порядок, если вы используете

 class Meta:
    fields = (
        'field1', 'field2'
    )
    ordered = True
  

Чтобы исправить это, я использовал это:

 # Remove None fields
@pre_dump
def remove_skip_values(self, data):
    return {
        key: value for key, value in data.items()
        if value is not None
    }
  

Это работает для моего справочника объектов

Комментарии:

1. Это более надежный выбор! Для меня должно быть принято!

2. можем ли мы сделать это необязательным с помощью начального параметра remove_none , например MySchema(remove_none= True).dump(obj)?

3. Я получаю сообщение об ошибке: remove_skip_values() got an unexpected keyword argument 'many'

4. @chrisinmtown: Я получил то же самое. Я исправил это, добавив **kwargs в список параметров для remove_skip_values . Кроме того, я обнаружил, что использование @pre_dump конфликтовало с имеющимся у меня хуком post_load, который преобразует данные в экземпляр класса. Переключение на @post_dump исправило это.