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启动办公软件进程(word、excel、ppt、以及wps的et、wps、wpp)
Apr 09 Python
跟老齐学Python之让人欢喜让人忧的迭代
Oct 02 Python
Python实现基本数据结构中队列的操作方法示例
Dec 04 Python
使用apidocJs快速生成在线文档的实例讲解
Feb 07 Python
Python绘制并保存指定大小图像的方法
Jan 10 Python
python用for循环求和的方法总结
Jul 08 Python
深入了解Django中间件及其方法
Jul 26 Python
Python中list的交、并、差集获取方法示例
Aug 01 Python
Python协程操作之gevent(yield阻塞,greenlet),协程实现多任务(有规律的交替协作执行)用法详解
Oct 14 Python
python随机模块random使用方法详解
Feb 14 Python
python为什么会环境变量设置不成功
Jun 23 Python
Python 视频画质增强
Apr 28 Python
python实现三次密码验证的示例
Django一小时写出账号密码管理系统
python中的被动信息搜集
Apr 29 #Python
Python基础之元类详解
Apr 29 #Python
教你怎么用Python监控愉客行车程
Django程序的优化技巧
Apr 29 #Python
教你怎么用Python实现多路径迷宫
You might like
php中通过正则表达式下载内容中的远程图片的函数代码
2012/01/10 PHP
解析web文件操作常见安全漏洞(目录、文件名检测漏洞)
2013/06/29 PHP
学习php设计模式 php实现享元模式(flyweight)
2015/12/07 PHP
yii2缓存Caching基本用法示例
2016/07/18 PHP
用js实现多域名不同文件的调用方法
2007/01/12 Javascript
Javascript 函数对象的多重身份
2009/06/28 Javascript
用Greasemonkey 脚本收藏网站会员信息到本地
2009/10/26 Javascript
jquery 防止表单重复提交代码
2010/01/21 Javascript
ExtJS 设置级联菜单的默认值
2010/06/13 Javascript
判断客户浏览器是否支持cookie的示例代码
2013/12/23 Javascript
instanceof和typeof运算符的区别详解
2014/01/06 Javascript
js 实现浏览历史记录示例
2014/04/20 Javascript
JavaScript与HTML的结合方法详解
2015/11/23 Javascript
Bootstrap轮播插件中图片变形的终极解决方案 使用jqthumb.js
2016/07/10 Javascript
AngularJS的$location使用方法详解
2017/10/19 Javascript
bootstrap select下拉搜索插件使用方法详解
2017/11/23 Javascript
解决在vue项目中webpack打包后字体不生效的问题
2018/09/01 Javascript
vue中使用v-model完成组件间的通信
2019/08/22 Javascript
微信小程序 wx:for 与 wx:for-items 与 wx:key的正确用法
2020/05/19 Javascript
Python素数检测实例分析
2015/06/15 Python
python生成随机图形验证码详解
2017/11/08 Python
Python基于PyGraphics包实现图片截取功能的方法
2017/12/21 Python
教你使用python实现微信每天给女朋友说晚安
2018/03/23 Python
numpy中的delete删除数组整行和整列的实例
2018/05/09 Python
django1.11.1 models 数据库同步方法
2018/05/30 Python
基于python实现的百度音乐下载器python pyqt改进版(附代码)
2019/08/05 Python
Python之数据序列化(json、pickle、shelve)详解
2019/08/30 Python
python中的selenium安装的步骤(浏览器自动化测试框架)
2020/03/17 Python
python使用hdfs3模块对hdfs进行操作详解
2020/06/06 Python
经验丰富程序员才知道的8种高级Python技巧
2020/07/27 Python
米兰网婚纱礼服法国网上商店:Milanoo法国
2016/08/20 全球购物
Ruby如何定义一个类
2012/10/08 面试题
5.1手机促销活动
2014/01/17 职场文书
幼儿园园长安全责任书
2015/05/08 职场文书
彻底理解golang中什么是nil
2021/04/29 Golang
JavaScript流程控制(循环)
2021/12/06 Javascript