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程序的执行效率的教程
Apr 02 Python
Python中使用strip()方法删除字符串中空格的教程
May 20 Python
python模块之sys模块和序列化模块(实例讲解)
Sep 13 Python
Java编程迭代地删除文件夹及其下的所有文件实例
Feb 10 Python
python顺序的读取文件夹下名称有序的文件方法
Jul 11 Python
python 多线程串行和并行的实例
Feb 22 Python
详解Python的数据库操作(pymysql)
Apr 04 Python
Python3.5面向对象与继承图文实例详解
Apr 24 Python
利用PyCharm Profile分析异步爬虫效率详解
May 08 Python
Django框架实现分页显示内容的方法详解
May 10 Python
Django-rest-framework中过滤器的定制实例
Apr 01 Python
python实现飞船大战
Apr 24 Python
python实现三次密码验证的示例
Django一小时写出账号密码管理系统
python中的被动信息搜集
Apr 29 #Python
Python基础之元类详解
Apr 29 #Python
教你怎么用Python监控愉客行车程
Django程序的优化技巧
Apr 29 #Python
教你怎么用Python实现多路径迷宫
You might like
PHP return语句的另一个作用
2014/07/30 PHP
Yii框架调试心得--在页面输出执行sql语句
2014/12/25 PHP
什么是PHP7中的孤儿进程与僵尸进程
2019/04/14 PHP
thinkphp框架实现路由重定义简化url访问地址的方法分析
2020/04/04 PHP
Whatever:hover 无需javascript让IE支持丰富伪类
2010/06/29 Javascript
一起来写段JS drag拖动代码
2010/12/09 Javascript
javascript学习笔记(十九) 节点的操作实现代码
2012/06/20 Javascript
Javascript实现鼠标右键特色菜单
2015/08/04 Javascript
jQuery实现带滑动条的菜单效果代码
2015/08/26 Javascript
JS实现仿FLASH效果的竖排导航代码
2015/09/15 Javascript
详解JavaScript编程中正则表达式的使用
2015/10/25 Javascript
JS实现的多张图片轮流播放幻灯片效果
2016/07/22 Javascript
JS简单验证上传文件类型的方法
2017/04/17 Javascript
Vue父子模版传值及组件传值的三种方法
2017/11/27 Javascript
浅析从vue源码看观察者模式
2018/01/29 Javascript
使用eslint和githooks统一前端风格的技巧
2020/07/29 Javascript
[03:30]DOTA2完美“圣”典精彩集锦
2016/12/27 DOTA
Python引用模块和查找模块路径
2016/03/17 Python
深入理解python函数递归和生成器
2016/06/06 Python
pip命令无法使用的解决方法
2018/06/12 Python
python 拼接文件路径的方法
2018/10/23 Python
Python 多维List创建的问题小结
2019/01/18 Python
一行python实现树形结构的方法
2019/08/09 Python
Centos7下源码安装Python3 及shell 脚本自动安装Python3的教程
2020/03/07 Python
Prometheus开发中间件Exporter过程详解
2020/11/30 Python
美国电子产品主要品牌的授权在线零售商:DataVision
2019/03/23 全球购物
高级护理专业毕业生推荐信
2013/12/25 职场文书
《从现在开始》教学反思
2014/04/15 职场文书
党员群众路线自我剖析材料
2014/10/06 职场文书
信访工作汇报材料
2014/10/27 职场文书
2014年高中班主任工作总结
2014/11/08 职场文书
2015重阳节座谈会主持词
2015/07/30 职场文书
导游词之太原天龙山
2020/01/02 职场文书
MySQL触发器的使用
2021/05/24 MySQL
对Keras自带Loss Function的深入研究
2021/05/25 Python
如何给HttpServletRequest增加消息头
2021/06/30 Java/Android