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数据操作方法封装类实例
Jun 23 Python
1分钟快速生成用于网页内容提取的xslt
Feb 23 Python
python的concat等多种用法详解
Nov 28 Python
python用fsolve、leastsq对非线性方程组求解
Dec 15 Python
python+mysql实现教务管理系统
Feb 20 Python
python使用Paramiko模块实现远程文件拷贝
Apr 30 Python
python GUI库图形界面开发之PyQt5信号与槽多窗口数据传递详细使用方法与实例
Mar 08 Python
无需压缩软件,用python帮你操作压缩包
Aug 17 Python
Python colormap库的安装和使用详情
Oct 06 Python
Python中对象的比较操作==和is区别详析
Feb 12 Python
详解Django中的FBV和CBV对比分析
Mar 01 Python
Pytorch可视化的几种实现方法
Jun 10 Python
python实现三次密码验证的示例
Django一小时写出账号密码管理系统
python中的被动信息搜集
Apr 29 #Python
Python基础之元类详解
Apr 29 #Python
教你怎么用Python监控愉客行车程
Django程序的优化技巧
Apr 29 #Python
教你怎么用Python实现多路径迷宫
You might like
php5 and xml示例
2006/11/22 PHP
PHP命名空间(Namespace)简明教程
2014/06/11 PHP
Mac版PhpStorm之XAMPP整合apache服务器配置的图文教程详解
2016/10/13 PHP
yii2中LinkPager增加总页数和总记录数的实例
2017/08/28 PHP
Laravel框架实现超简单的分页效果示例
2019/02/08 PHP
解决Laravel自定义类引入和命名空间的问题
2019/10/15 PHP
基于Jquery的动态创建DOM元素的代码
2010/12/28 Javascript
给artDialog 5.02 增加ajax get功能详细介绍
2012/11/13 Javascript
js 处理数组重复元素示例代码
2013/12/27 Javascript
IE中getElementsByName()对有些元素无效的解决方案
2014/09/28 Javascript
基于zepto.js实现仿手机QQ空间的大图查看组件ImageView.js详解
2015/03/05 Javascript
JS基于myFocus库实现各种功能的tab选项卡切换效果
2015/09/19 Javascript
JS实现同一个网页布局滑动门和TAB选项卡实例
2015/09/23 Javascript
利用jQuery的动画函数animate实现豌豆发射效果
2016/08/28 Javascript
jQuery实现的浮动层div浏览器居中显示效果
2017/02/03 Javascript
angularjs项目的页面跳转如何实现(5种方法)
2017/05/25 Javascript
详解Chai.js断言库API中文文档
2018/01/31 Javascript
Vue 中使用vue2-highcharts实现曲线数据展示的方法
2018/03/05 Javascript
VUE的history模式下除了index外其他路由404报错解决办法
2019/08/21 Javascript
php使用递归与迭代实现快速排序示例
2014/01/23 Python
scrapy爬虫实例分享
2017/12/28 Python
python获取代理IP的实例分享
2018/05/07 Python
使用Python实现租车计费系统的两种方法
2018/09/29 Python
Python+Selenium使用Page Object实现页面自动化测试
2019/07/14 Python
详解python中的闭包
2020/09/07 Python
Python中Pyspider爬虫框架的基本使用详解
2021/01/27 Python
美国糖果店:Sugarfina
2019/02/21 全球购物
超级英雄、电影和电视、乐队和音乐T恤:Loud Clothing
2019/09/01 全球购物
俄罗斯购买剧院和演唱会门票网站:Parter.ru
2019/11/09 全球购物
日本整理专家Marie Kondo的官方在线商店:KonMari
2020/06/29 全球购物
培训心得体会
2013/12/29 职场文书
仓库规划计划书
2014/04/28 职场文书
安全生产月活动总结
2014/05/04 职场文书
我的中国梦演讲稿1000字
2014/08/19 职场文书
家装业务员岗位职责
2015/04/03 职场文书
简历自我评价:教师师德表现自我评价
2019/04/24 职场文书