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 04 Python
python中pandas.DataFrame的简单操作方法(创建、索引、增添与删除)
Mar 12 Python
分析python切片原理和方法
Dec 19 Python
python实现用户管理系统
Jan 10 Python
Python运行不显示DOS窗口的解决方法
Oct 22 Python
对Python通过pypyodbc访问Access数据库的方法详解
Oct 27 Python
python中数组和矩阵乘法及使用总结(推荐)
May 18 Python
Appium+python自动化怎么查看程序所占端口号和IP
Jun 14 Python
导入tensorflow时报错:cannot import name 'abs'的解决
Oct 10 Python
Python闭包及装饰器运行原理解析
Jun 17 Python
使用Keras建立模型并训练等一系列操作方式
Jul 02 Python
基于Python爬取fofa网页端数据过程解析
Jul 13 Python
python实现三次密码验证的示例
Django一小时写出账号密码管理系统
python中的被动信息搜集
Apr 29 #Python
Python基础之元类详解
Apr 29 #Python
教你怎么用Python监控愉客行车程
Django程序的优化技巧
Apr 29 #Python
教你怎么用Python实现多路径迷宫
You might like
echo, print, printf 和 sprintf 区别
2006/12/06 PHP
php多层数组与对象的转换实例代码
2013/08/05 PHP
ci检测是ajax还是页面post提交数据的方法
2014/11/10 PHP
Laravel等框架模型关联的可用性浅析
2019/12/15 PHP
PHP的imageTtfText()函数深入详解
2021/03/03 PHP
用js实现层随着内容大小动态渐变改变 推荐
2009/12/19 Javascript
Ajax 数据请求的简单分析
2011/04/05 Javascript
JavaScript学习笔记之JS事件对象
2015/01/22 Javascript
谈谈我对JavaScript原型和闭包系列理解(随手笔记9)
2015/12/24 Javascript
JS+CSS3实现超炫的散列画廊特效
2016/07/16 Javascript
详解Sea.js中Module.exports和exports的区别
2017/02/12 Javascript
jQuery实现弹窗下底部页面禁止滑动效果
2017/12/19 jQuery
解决使用layui对select append元素无效或者未及时更新的问题
2019/09/18 Javascript
Vue.js中的高级面试题及答案
2020/01/13 Javascript
使用python搭建Django应用程序步骤及版本冲突问题解决
2013/11/19 Python
Python批量修改文件后缀的方法
2014/01/26 Python
python遍历文件夹并删除特定格式文件的示例
2014/03/05 Python
对dataframe进行列相加,行相加的实例
2018/06/08 Python
python 从csv读数据到mysql的实例
2018/06/21 Python
python实现计数排序与桶排序实例代码
2019/03/28 Python
Django实现跨域的2种方法
2019/07/31 Python
Python中Unittest框架的具体使用
2019/08/27 Python
Django 请求Request的具体使用方法
2019/11/11 Python
Python 去除字符串中指定字符串
2020/03/05 Python
python求前n个阶乘的和实例
2020/04/02 Python
NFL墨西哥官方商店:Tienda NFL
2017/11/28 全球购物
美国最大的在线寄售和旧货店:Swap.com
2018/08/27 全球购物
Farfetch美国:奢侈品牌时尚购物平台
2019/05/02 全球购物
简述你对Statement,PreparedStatement,CallableStatement的理解
2013/03/25 面试题
2014小学数学教师个人工作总结
2014/12/18 职场文书
春秋淹城导游词
2015/02/11 职场文书
先进个人自荐书
2015/03/06 职场文书
食品安全责任书范本
2015/05/09 职场文书
2016教师廉洁教育心得体会
2016/01/13 职场文书
话题作文之成长
2019/12/09 职场文书
vue项目配置sass及引入外部scss文件
2022/04/14 Vue.js