原文:https://blog.csdn.net/chen__an/article/details/85296806
1、前言
客户端的开发语言很可能与后台的开发语言不一样。这时我们需要后台能够提供可以跨平台跨语言的一种标准的数据交换格式供 前后端沟通(这就是Web API的作用)。 如今大家最常用的跨平台跨语言数据交换格式就是JSON(JavaScript Object Notation)了。 JSON是一种文本序列化格式(它输出的是unicode文件,大多数时候会被编码为utf-8),人们很容易进行阅读和编写。它的dumps方法很有用, 能很容易将字典dict类型数据转化为json格式,然后还有很多类型的数据(如日期,集合, 自定义的类和Django的QuerySet类型),我们需要自定义 序列化方法才能将它们转化为json格式。今天小编我就来对python的json模块做下总结,并详细介绍如何把不同类型的数据json序列化。
2、关于序列化
每种编程语言都有各自的数据类型, 将属于自己语言的数据类型或对象转换为可通过网络传输或可以存储到本地磁盘的数据格式
(如:XML、JSON或特定格式的字节串)的过程称为序列化(seralization);反之则称为反序列化。
3、python的json模块
python自带的json库(无需额外安装), 主要包含了dumps, loads, dump和load四种方法其作用分别如下所示: -json.loads() - 将json字符串转换为python数据类型 -json.dumps() - 将python数据类型转化为json字符串 -json.dump() - 将python输入转化为json格式存入磁盘文件 -json.load() - 将磁盘文件中json格式数据转换为python数据类型
python数据格式与json数据格式对应转换关系:
注意到了吗? 还有很多python数据类型(set, datetime)不在上表中;
4、json模块的dumps方法介绍
json模块的dumps方法可以将python常用数据格式转化为json格式。该方法还提供了很多可选参数如ident, separators, ensure_ascii, sort_keys和default参数。这些参数都非常有用;
# 生成的json格式数据外面都加了单引号,说明dict类型数据已经转化成了json字符串。 >>> import json >>> json.dumps({"name":"w","age":22}) '{"name": "w", "age": 22}'
如果一个dict很长,生成的json字符串会非常长,这时我们可以设置indent参数使生成的json格式数据更优美,更容易人们阅读:
>>> print(json.dumps({"name":"John", "score": 112}, indent=4)) { "name": "John", "score": 112 } >>> json.dumps({"name":"John", "score": 112}, indent=4) '{ "name": "John", "score": 112 }'
然而使用indent参数的代价是json字符串里增加了额外的空白,机器阅读根本不需要它们;
即使不用indent参数,你会发现dumps生成的json字符串中的','号和':'号分隔符后都会附加一个默认空白字符,我们可以
通过separators参数重新指定分隔符,从而去除无用的空白字符,如下所示。这样可以减少无用数据的的传输,节省带宽增加数据传输速度。
>>> import json >>> json.dumps({"name":"John", "score": 112}) '{"name": "John", "score": 112}' # 使用separators选项 >>> json.dumps({"name":"John", "score": 112}, separators=(',',':')) '{"name":"John","score":112}'
如果字符串有非ASCII字符(比如中文),它们在json序列化时都会被转义成'uXXXX'组成的ascii字符串。
如果想得到更加易读的字符串,可以设置ensure_ascii=False
>>> import json >>> json.dumps({"Name":"小明", "Age": 16}) '{"Name": "\u5c0f\u660e", "Age": 16}' # 设置ensure_ascii=False >>> json.dumps({"Name":"小明", "Age": 16}, ensure_ascii=False) '{"Name": "小明", "Age": 16}'
一般的dict默认是无序的,你还可以设置sort_keys = True对生成的json格式数据进行排序,这里就不演示了。default参数我们后面会重点介绍。
5、dump,loads和load方法
dump方法用于将生成的json数据写入一个磁盘文件。其用法和dumps类似,唯一不同的是需要指定需要写入的文件,具体用法如下所示:
import json with open("json.txt", 'w') as f: json.dump({"Name":"小明", "Age": 16}, f, ensure_ascii=True)
json的loads方法用于将json格式数据转化为python格式,实现数据的反序列化,如下所示。千万别忘了在json符串外的单引号:
>>> import json >>> json.loads('{"Name": "小明", "Age": 16}') {'Name': '小明', 'Age': 16}
json的load方法与loads用法相似,不过它需要指定存有json数据的文件
>>> import json >>> with open("json.txt", 'r') as f: json.load(f)
6、很多python格式数据不能直接被dumps方法序列化
很多python数据类型(比如日期,集合和自定义的类)并不能直接被dumps方法序列化,这时会
出现 xxx is not JSON serializable的错误,如下面代码所示。当出现类似错误时,我们有两种解决方案。
-
通过数据类型转换函数实现
-
通过继承JSONEncoder和JSONDecoder类实现
出错的代码:
>>> import json >>> from datetime import datetime # DateTime类型 >>> json.dumps({"date":datetime.now()}) Traceback (most recent call last): TypeError: Object of type 'datetime' is not JSON serializable # 自定义的User类 >>> class User(object):
def __init__(self, name):
self.name = name >>> json.dumps(User("John")) Traceback (most recent call last): TypeError: Object of type 'User' is not JSON serializable
解决方案一: 编写数据类型转换函数
该方法的工作原理是先编写数据类型转化函数,通过设置dumps方法里的default参数调用格式转化函数,将dumps方法不支持的数类型先转化为字符串格式,再实现json序列化。
# 将datetime格式数据json化 >> > import json >> > from datetime import datetime >> > def date_to_str(obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
return TypeError >> > json.dumps({"date": datetime.now()}, default=date_to_str) '{"date": "2018-09-22 21:25:42"}' # 将set类型数据json化 >> > import json >> > set_data = {'my_set': {1, 2, 3}} >> > def set_to_list(obj):
if isinstance(obj, set):
return list(obj)
raise TypeError >> > result = json.dumps(set_data, default=set_to_list)
对于我们自定义的类, 使用dumps方法时我们一般要先编写obj_to_dict方法,将object转化为字典dict再JSON序列化。
同理,使用loads方法对json数据反序列化时,我们还需要编写dict_to_obj方法,通过default参数调用。
下面这2段代码是比较通用的对象(object)与字典(dict)互转的经典代码:
# 将自定义的类转化为字典,dumps方法使用 def obj_to_dict(obj):
d = {}
d['__class__'] = obj.__class__.__name__
d['__module__'] = obj.__module__
d.update(obj.__dict__)
return d # 将字典转化为自定义的类,loads方法使用 def dict_to_obj(d):
if '__class__' in d:
class_name = d.pop('__class__')
module_name = d.pop('__module__')
module = __import__(module_name)
class_ = getattr(module, class_name)
args = dict((key.encode('ascii'), value) for key, value in d.items())
instance = class_(**args)
else:
instance = d
return instance
解决方案二: 继承JSONEncoder类和JSONDecode类
另一个解决方案是继承JSONEncoder类和JSONDecode类定义自己的编码Encoder类,然后使用cls=MyEncoder,来调用编码器。
比如下例中我们定义了自己的DateTimeEncoder,将日期类型数据序列化。
from datetime import datetime, date import json class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
return json.JSONEncoder.default(self, obj)
json_data = {'num': 1, 'date': datetime.now()} print(json.dumps(json_data, cls=DateTimeEncoder))
对于自定义的对象,我们也可以通过继承JSONEncoder类实现它的序列化,如下所示:
import json class User(object):
def __init__(self, name):
self.name = name class MyJSONEncoder(json.JSONEncoder):
def default(self, obj):
d = {}
d['__class__'] = obj.__class__.__name__
d['__module__'] = obj.__module__
d.update(obj.__dict__)
return d user = User("John") json.dumps(user, cls=MyJSONEncoder))
对于简单的数据序列化,方案一更容易理解,代码也更少。但当需要传输的数据很大时,使用继承JSONEncoder类来实现序列化时有个很大的好处,
就是可以通过iterencode()方法把一个很大的数据对象分多次进行序列化,这对于网络持续传输或写入大的文件非常有用。如下所示:
>>> for chunk in MyJSONEncoder().iterencode(big_object): ... print(chunk)