使用 Marshmallow 的嵌套模式自动字典键解析
Automatic dictionary key resolution with nested schemas using Marshmallow
我有一个 Marshmallow 模式,其中对象使用键来引用在结构的另一部分的字典中定义的对象。我希望在反序列化对象时自动解析密钥。我怎样才能以惯用的方式在 Marshmallow 中实现这种效果?
目前的解决方法是手动解析所有引用,但这看起来很笨拙,因为 Marshmallow 的声明性质应该能够自动为我们完成。
请注意,Marshmallow-SQLAlchemy 在 columns are declared as relationships 时支持这种(反)序列化,它会自动为我们执行此操作,但我想使用 JSON 数据来执行此操作。
这是我想要实现的示例,fields.Relationship
是尚不存在的东西:
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
friends = fields.Relationship('self', path="AddressBook.contacts", many=True)
class AddressBookSchema(Schema):
contacts = nested.Dict(keys=fields.String(), values=fields.Nested(UserSchema))
# ... create ``user`` ...
serialized_data = AddressBookSchema().dump(user)
pprint(serialized_data)
# "contacts": {
# "Steve": {
# "name": "Steve",
# "email": "steve@example.com",
# "friends": ["Mike"]
# },
# "Mike": {
# "name": "Mike",
# "email": "mike@example.com",
# "friends": []
# }
deserialized_data = UserSchema().load(result)
pprint(deserialized_data)
# "contacts": {
# "Steve": {
# "name": "Steve",
# "email": "steve@example.com",
# "friends": [ {"name": "Mike", "email": "mike@example.com"]
# },
# "Mike": {
# "name": "Mike",
# "email": "mike@example.com",
# "friends": []
# }
为什么不简单地用 post_load
钩子传输中间数据:
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
friends = fields.List(fields.String())
class AddressBookSchema(Schema):
contacts = fields.Dict(keys=fields.String(), values=fields.Nested(UserSchema))
@post_load
def trans_friends(self, item):
for name in item['contacts']:
item['contacts'][name]['friends'] = [item['contacts'][n] for n in item['contacts'][name]['friends']]
data = """
{
"contacts": {
"Steve": {
"name": "Steve",
"email": "steve@example.com",
"friends": ["Mike"]
},
"Mike": {
"name": "Mike",
"email": "mike@example.com",
"friends": []
}
}
}
"""
deserialized_data = AddressBookSchema().loads(data)
pprint(deserialized_data)
产量:
UnmarshalResult(data={'contacts': {'Steve': {'name': 'Steve', 'email': 'steve@example.com', 'friends': [{'name': 'Mike', 'email': 'mike@example.com', 'friends': []}]}, 'Mike': {'name': 'Mike', 'email': 'mike@example.com', 'friends': []}}}, errors={})
我有一个 Marshmallow 模式,其中对象使用键来引用在结构的另一部分的字典中定义的对象。我希望在反序列化对象时自动解析密钥。我怎样才能以惯用的方式在 Marshmallow 中实现这种效果?
目前的解决方法是手动解析所有引用,但这看起来很笨拙,因为 Marshmallow 的声明性质应该能够自动为我们完成。
请注意,Marshmallow-SQLAlchemy 在 columns are declared as relationships 时支持这种(反)序列化,它会自动为我们执行此操作,但我想使用 JSON 数据来执行此操作。
这是我想要实现的示例,fields.Relationship
是尚不存在的东西:
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
friends = fields.Relationship('self', path="AddressBook.contacts", many=True)
class AddressBookSchema(Schema):
contacts = nested.Dict(keys=fields.String(), values=fields.Nested(UserSchema))
# ... create ``user`` ...
serialized_data = AddressBookSchema().dump(user)
pprint(serialized_data)
# "contacts": {
# "Steve": {
# "name": "Steve",
# "email": "steve@example.com",
# "friends": ["Mike"]
# },
# "Mike": {
# "name": "Mike",
# "email": "mike@example.com",
# "friends": []
# }
deserialized_data = UserSchema().load(result)
pprint(deserialized_data)
# "contacts": {
# "Steve": {
# "name": "Steve",
# "email": "steve@example.com",
# "friends": [ {"name": "Mike", "email": "mike@example.com"]
# },
# "Mike": {
# "name": "Mike",
# "email": "mike@example.com",
# "friends": []
# }
为什么不简单地用 post_load
钩子传输中间数据:
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
friends = fields.List(fields.String())
class AddressBookSchema(Schema):
contacts = fields.Dict(keys=fields.String(), values=fields.Nested(UserSchema))
@post_load
def trans_friends(self, item):
for name in item['contacts']:
item['contacts'][name]['friends'] = [item['contacts'][n] for n in item['contacts'][name]['friends']]
data = """
{
"contacts": {
"Steve": {
"name": "Steve",
"email": "steve@example.com",
"friends": ["Mike"]
},
"Mike": {
"name": "Mike",
"email": "mike@example.com",
"friends": []
}
}
}
"""
deserialized_data = AddressBookSchema().loads(data)
pprint(deserialized_data)
产量:
UnmarshalResult(data={'contacts': {'Steve': {'name': 'Steve', 'email': 'steve@example.com', 'friends': [{'name': 'Mike', 'email': 'mike@example.com', 'friends': []}]}, 'Mike': {'name': 'Mike', 'email': 'mike@example.com', 'friends': []}}}, errors={})