在介绍marshmallow模块前,先简单介绍下什么是序列化与反序列化。
序列化是指将数据对象转化为可储存或可传输的数据类型,也就是将Python的object对象转化为str, dict, list等;而反序列化是指将可存储或可传输的数据类型转化为数据对象,也就是将Python中的str, dict, list等转化为object对象。
marshmallow模块是Python中方便实现序列化与反序列化的第三方模块。本文将会介绍如何使用marshmallow实现序列化与反序列化,marshmallow的版本为3.7.1。
我们先创建author.py
脚本,它创建了一个Author类,具有三个数据属性:name, email和created_at(创建时间),代码如下:
# -*- coding: utf-8 -*-
import datetime
# 序列化的类
class User(object):
def __init__(self, name, email):
self.name = name
self.email = email
self.created_at = datetime.datetime.now()
def __repr__(self):
return '' .format(self=self)
在marshmallow中引入Schema类和fields,并创建一个UserSchema继承自Schema类,以及相应的数据结构,利用Schema的dump方法进行序列化,代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, pprint
from author import User
# 反序列化的类
class UserSchema(Schema):
name = fields.Str()
email = fields.Email()
created_at = fields.DateTime()
user = User(name="Jclian91", email="[email protected]")
print(user)
schema = UserSchema()
result = schema.dump(user) # obj -> dict
pprint(result)
print(type(result))
输出结果如下:
{'created_at': '2020-09-07T21:08:08.815759',
'email': '[email protected]',
'name': 'Jclian91'}
可以看到user变量的类型为User类,而序列化后的result变量类型为dict。
这里我们需要稍微区分一下schema的dump方法和dumps方法:dump()方法返回的是dict格式,而dumps()方法返回的是JSON字符串。如果我们把上述的dump改成dumps,输出结果如下:
('{"name": "Jclian91", "created_at": "2020-09-07T21:10:44.214661", "email": '
'"[email protected]"}')
如果我们再注意一下UserSchema,它有两个参数为only和exclude,only返回的输出结果只包含only列表中的类属性,而exclude正好相反,它是排除exclude列表中的类属性。如果我们把schema = UserSchema()
改成schema = UserSchema(only=('email', 'name'))
,则返回的字典中只有name和email,输出结果如下:
{'email': '[email protected]', 'name': 'Jclian91'}
上述修改语句的效果跟schema = UserSchema(exclude=("created_at", ))
一致。
这里介绍如何将单个对象的dict或JSON字符串转化为Python对象。
在反序列化类UserSchema类中引入post_load装饰器,并创建make_user函数将传入数据转化为User类,代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, post_load
from author import User
# 反序列化的类
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
created_at = fields.DateTime()
@post_load
def make_user(self, data, **kwargs):
return User(**data)
user_data = {
'name': 'Jclian91', 'email': '[email protected]'}
schema = UserSchema()
result = schema.load(user_data)
print(result)
print(type(result))
print("name: {}, email: {}, create_time: {}".format(result.name, result.email, result.created_at))
输出结果如下:
name: Jclian91, email: [email protected], create_time: 2020-09-07 21:21:04.384272
可以看到,user_data为字典,而result为User类。
这里稍微做一下说明,同上面序列化的dump和dumps方法对应,load方法用来加载字典,而loads方法用来加载JSON字符串。
上面介绍了单个对象的情形,接下来将讨论多个对象的情形。
dump方法可以接受对象列表,但UserSchema中需要将many参数设置为True,代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, pprint
from author import User
# 反序列化的类
class UserSchema(Schema):
name = fields.Str()
email = fields.Email()
created_at = fields.DateTime()
user1 = User(name="Jclian91", email="[email protected]")
user2 = User(name="Jclian92", email="[email protected]")
user3 = User(name="Jclian93", email="[email protected]")
users = [user1, user2, user3]
schema = UserSchema(many=True) # obj -> list
result = schema.dump(users)
pprint(result)
print(type(result))
输出结果如下:
[{'created_at': '2020-09-07T21:26:09.862986',
'email': '[email protected]',
'name': 'Jclian91'},
{'created_at': '2020-09-07T21:26:09.862986',
'email': '[email protected]',
'name': 'Jclian92'},
{'created_at': '2020-09-07T21:26:09.862986',
'email': '[email protected]',
'name': 'Jclian93'}]
同理,多个对象的反序列化时,dump方法可以接受多个字典组成的列表,但UserSchema中的many参数设置为True。代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, post_load
from author import User
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
created_at = fields.DateTime()
@post_load
def make_user(self, data, **kwargs):
return User(**data)
user_data = [{
'name': 'Ronnie', 'email': '[email protected]'},
{
'name': 'Amen', 'email': '[email protected]'},
{
'name': 'Uno', 'email': '[email protected]'}]
schema = UserSchema(many=True)
result = schema.load(user_data)
print(result)
print(type(result), type(result[0]))
输出结果如下:
[, , ]
可以看到,result确实为多个类实例组成的列表。
数据验证指的是在反序列化的时候,对数据进行验证,之所以能进行验证,是因为我们在定义Schema的时候,对每个字段(field)规定了数据类型,比如email字段为fields.Email()类型,这就意味着email数据必须符合Email。
marshmallow提供了数据验证方法validate(),同时我们在反序列化的时候也可以捕捉到数据验证相关的错误。
数据验证的代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, post_load, ValidationError
from author import User
# 反序列化的类
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
created_at = fields.DateTime()
@post_load
def make_user(self, data, **kwargs):
return User(**data)
schema = UserSchema()
try:
res = UserSchema().load({
"name": "ttty", "email": "[email protected]"})
print(res)
except ValidationError as e:
print("错误信息:{}, 合法数据:{}".format(e.messages, e.valid_data))
以上的数据结构符合字段定义,所以反序列化正常,输出结果如下:
但是当我们将[email protected]
改成ttty
的时候,程序就会报错,因为ttty
不符合Email格式,反序列化的数据验证不通过,程序输出如下:
错误信息:{'email': ['Not a valid email address.']}, 合法数据:{'name': 'ttty'}
而name字段仍然是有效的。
对多个字典进行反序列化,代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, post_load, ValidationError
from author import User
# 反序列化的类
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
created_at = fields.DateTime()
@post_load
def make_user(self, data, **kwargs):
return User(**data)
schema = UserSchema()
user_data = [
{
'email': '[email protected]', 'name': 'Mick'},
{
'email': 'invalid', 'name': 'Invalid'},
{
'name': 'Keith'},
{
'email': '[email protected]'},
]
try:
schema = UserSchema(many=True)
res = schema.load(user_data)
except ValidationError as e:
print("错误信息:{}, 合法数据:{}".format(e.messages, e.valid_data))
输出结果如下:
错误信息:{1: {'email': ['Not a valid email address.']}}, 合法数据:[{'name': 'Mick', 'email': '[email protected]'}, {'name': 'Invalid'}, {'name': 'Keith'}, {'email': '[email protected]'}]
可以看到只有第二个字典在反序列化的时候报错。从中,我们也可以发现,对缺失属性不会进行验证。
如果需要对属性进行缺失验证,则在schema中规定required参数,即表明该参数是必要的,不可缺失。实例代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, post_load, ValidationError
from author import User
# 反序列化的类
class UserSchema(Schema):
name = fields.String(required=True)
email = fields.Email()
created_at = fields.DateTime()
@post_load
def make_user(self, data, **kwargs):
return User(**data)
schema = UserSchema()
user_data = [
{
'email': '[email protected]', 'name': 'Mick'},
{
'email': 'invalid', 'name': 'Invalid'},
{
'name': 'Keith'},
{
'email': '[email protected]'},
]
try:
schema = UserSchema(many=True)
res = schema.load(user_data)
except ValidationError as e:
print("错误信息:{}, 合法数据:{}".format(e.messages, e.valid_data))
我们在定义Schema的时候,规定了name字段是不可缺失的,因此输出结果如下:
错误信息:{1: {‘email’: [‘Not a valid email address.’]}, 3: {‘name’: [‘Missing data for required field.’]}}, 合法数据:[{‘email’: ‘[email protected]’, ‘name’: ‘Mick’}, {‘name’: ‘Invalid’}, {‘name’: ‘Keith’}, {‘email’: ‘[email protected]’}]
可以看到,第四个字典因为缺失name属性而无法通过数据验证。
在marshmallow中,还支持自定义的数据验证方法,实例代码如下:
# -*- coding: utf-8 -*-
from marshmallow import Schema, fields, ValidationError, validates, post_load
from author import User
# 反序列化的类
class UserSchema(Schema):
name = fields.String(required=True)
email = fields.Email()
created_time = fields.DateTime()
@post_load
def make_user(self, data, **kwargs):
return User(**data)
@validates("name")
def validate_name(self, value):
if len(value) <= 2:
raise ValidationError("name长度必须大于2位")
if len(value) >= 6:
raise ValidationError("name长度不能大于6位")
user_data = {
'name': 'Jclian91', 'email': '[email protected]'}
try:
res = UserSchema().load(user_data)
except ValidationError as e:
print(e.messages)
我们在UserSchema类中增加了validates装饰器,并对name字段进行验证,给出了自定义的数据验证方法,即name字段长度不得小于2,大于6,因此,程序输出结果如下:
{'name': ['name长度不能大于6位']}
本次分享到此结束,感谢大家阅读~