Python如何把不同类型数据的json序列化


Posted in Python onApril 30, 2021

现代网络应用Web APP或大型网站的后台一般只有一个,然后客户端却是各种各样的(iOS, android, 浏览器), 而且客户端的开发语言很可能与后台的开发语言不一样。这时我们需要后台能够提供可以跨平台跨语言的一种标准的数据交换格式供前后端沟通(这就是Web API的作用)。如今大家最常用的跨平台跨语言数据交换格式就是JSON(JavaScript Object Notation)了。JSON是一种文本序列化格式(它输出的是unicode文件,大多数时候会被编码为utf-8),人们很容易进行阅读和编写。python自带的dumps方法很有用,能很容易将字典dict类型数据转化为json格式,然后还有很多类型的数据(如日期,集合, 自定义的类和Django的QuerySet类型),我们需要自定义序列化方法才能将它们转化为json格式。今天小编我就来对python的json模块做下总结,并详细介绍如何把不同类型的数据json序列化。

何谓序列化(serialization)

每种编程语言都有各自的数据类型, 将属于自己语言的数据类型或对象转换为可通过网络传输或可以存储到本地磁盘的数据格式(如:XML、JSON或特定格式的字节串)的过程称为序列化(seralization);反之则称为反序列化。

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 JSON
dict Object
list, tuple array
str string
int, float,  numbers
True true
False false
None null

你注意到了吗? 还有很多python数据类型(set, datetime)不在上表中哦。

json的模块dumps方法介绍 - python数据的序列化

json模块的dumps方法可以将python常用数据格式转化为json格式。该方法还提供了很多可选参数如ident, separators, ensure_ascii, sort_keys和default参数。这些参数都非常有用,我们会稍后逐一介绍。

dumps(obj, skipkeys=False, ensure_ascii=True, 
      check_circular=True, allow_nan=True, 
      cls=None, indent=None, separators=None,
      default=None, sort_keys=False, **kw)

我们先来看看一个最简单的例子。你注意到了吗? 生成的json格式数据外面都加了单引号,这说明dict类型数据已经转化成了json字符串。

>>> import json
>>> json.dumps({"name":"John", "score": 112})
'{"name": "John", "score": 112}'

如果一个dict很长,生成的json字符串会非常长,这时我们可以设置indent参数使生成的json格式数据更优美,更容易人们阅读。代码如下所示:

>>> import json
>>> json.dumps({"name":"John", "score": 112}, indent=4)
'{\n    "name": "John",\n    "score": 112\n}'
>>> print(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参数我们后面会重点介绍。

json模块的dump,loads和load方法介绍

与dumps方法不同,json模块的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)

很多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)

Django特有数据类型序列化

Django编程就是是python编程,以上所介绍的序列化方法对django也是适用的。不同的是Django还有自己专属的数据类型比如QuerySet和ValueQuerySet类型数据,还提供了更便捷的serializers类。使用serializers类可以轻易将QuerySet格式的数据转化为json格式。

# Django Queryset数据 to Json
from django.core import serializers
data = serializers.serialize("json", SomeModel.objects.all())
data1 = serializers.serialize("json", SomeModel.objects.all(), fields=('name','id'))
data2 = serializers.serialize("json", SomeModel.objects.filter(field = some_value))

有时候我们只需要查询结果集的部分字段,可以使用values('字段名','字段名2')来要求返回的是哪些列的数据.但是返回来的是ValuesQuerySet对象而不是QuerySet对象。ValuesQuerySet对象不能用 serializers.serialize() 方法序列化成json, 需要先转换成list再用 json.dumps()方法序列化成json格式。示例代码如下所示:

import json
from django.core.serializers.json import DjangoJSONEncoder

queryset = myModel.objects.filter(foo_icontains=bar).values('f1', 'f2', 'f3')
data4 = json.dumps(list(queryset), cls=DjangoJSONEncoder)

django-rest-framework

如果你要利用django开发restful的web API, 为不同客户端提供序列化过的json格式数据,django-rest-framework才是你真正需要的序列化工具。与django自带的serializers类相比,rest framework支持用户验证,查询过滤和符合restful规范的url设计,我们后面会专门介绍。欢迎关注我的微信。

小结

我们介绍了何为JSON序列化以及python json模块的dumps, loads, dump和load方法。我们还介绍了如何将dumps方法不支持的数据格式(如日期,集合, 自定义的类和Django的QuerySet类型)如何通过要自定义格式转化函数和继承JsonEncoder类将它们转化为json格式。希望本文对你有所帮助。喜欢的给个赞吧!

以上就是Python如何把不同类型数据的json序列化的详细内容,更多关于python 数据json序列化的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
使用python检测手机QQ在线状态的脚本代码
Feb 10 Python
Python比较两个图片相似度的方法
Mar 13 Python
深入讨论Python函数的参数的默认值所引发的问题的原因
Mar 30 Python
Python使用ftplib实现简易FTP客户端的方法
Jun 03 Python
Python遍历目录并批量更换文件名和目录名的方法
Sep 19 Python
Python列表list操作符实例分析【标准类型操作符、切片、连接字符、列表解析、重复操作等】
Jul 24 Python
Python数据结构之栈、队列的实现代码分享
Dec 04 Python
python实现快速排序的示例(二分法思想)
Mar 12 Python
对Python中的@classmethod用法详解
Apr 21 Python
Python脚本完成post接口测试的实例
Dec 17 Python
Python爬虫:将headers请求头字符串转为字典的方法
Aug 21 Python
Python进程池与进程锁之语法学习
Apr 11 Python
python实现三次密码验证的示例
Django一小时写出账号密码管理系统
python中的被动信息搜集
Apr 29 #Python
Python基础之元类详解
Apr 29 #Python
教你怎么用Python监控愉客行车程
Django程序的优化技巧
Apr 29 #Python
教你怎么用Python实现多路径迷宫
You might like
PHP-Java-Bridge使用笔记
2014/09/22 PHP
Laravel实现定时任务的示例代码
2017/08/10 PHP
PHP实现动态压缩js与css文件的方法
2018/05/02 PHP
php使用fputcsv实现大数据的导出操作详解
2020/02/27 PHP
为jquery.ui.dialog 增加“在当前鼠标位置打开”的功能
2009/11/24 Javascript
jquery仿QQ商城带左右按钮控制焦点图片切换滚动效果
2013/06/27 Javascript
禁用Enter键表单自动提交实现代码
2014/05/22 Javascript
使用JavaScript进行进制转换将字符串转换为十进制
2014/09/21 Javascript
探寻Javascript执行效率问题
2014/11/12 Javascript
JavaScript常用的弹出广告及背投广告实现方法
2015/02/06 Javascript
JavaScript实现动画打开半透明提示层的方法
2015/04/21 Javascript
AngularJS ng-blur 指令详解及简单实例
2016/07/30 Javascript
jQuery Easy UI中根据第一个下拉框选中的值设置第二个下拉框是否可以编辑
2016/11/29 Javascript
vue2.0项目中使用Ueditor富文本编辑器示例代码
2017/08/14 Javascript
JS写XSS cookie stealer来窃取密码的步骤详解
2017/11/20 Javascript
详解JS转换数值函数Number()、parseInt()、parseFloat()
2018/08/24 Javascript
vue中使用rem布局代码详解
2019/10/30 Javascript
工作中常用js功能汇总
2020/11/07 Javascript
[01:32]DOTA2上海特锦赛现场采访:最想COS的英雄
2016/03/25 DOTA
Python实现抓取城市的PM2.5浓度和排名
2015/03/19 Python
tensorflow识别自己手写数字
2018/03/14 Python
python实现逆序输出一个数字的示例讲解
2018/06/25 Python
基于Python实现迪杰斯特拉和弗洛伊德算法
2020/05/27 Python
pytorch中交叉熵损失(nn.CrossEntropyLoss())的计算过程详解
2020/01/02 Python
浅谈django框架集成swagger以及自定义参数问题
2020/07/07 Python
CSS3 border-radius圆角的实现方法及用法详解
2020/09/14 HTML / CSS
英国最大的电脑零售连锁店集团:PC World
2016/10/10 全球购物
澳大利亚一站式数码相机商店:CameraPro
2020/03/09 全球购物
电气工程和自动化自荐信范文
2013/12/25 职场文书
黄金搭档广告词
2014/03/21 职场文书
企业理念标语
2014/06/09 职场文书
领导班子群众路线与四风问题对照检查材料思想汇报
2014/10/11 职场文书
结婚老公保证书
2015/02/26 职场文书
《我们的民族小学》教学反思
2016/02/19 职场文书
2016见义勇为事迹材料汇总
2016/03/01 职场文书
浙江省杭州市平均工资标准是多少?
2019/07/09 职场文书