详解Python对JSON中的特殊类型进行Encoder


Posted in Python onJuly 15, 2019

Python 处理 JSON 数据时,dumps 函数是经常用到的,当 JSON 数据中有特殊类型时,往往是比较头疼的,因为经常会报这样一个错误。

自定义编码类

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: wxnacy(wxnacy@gmail.com)

import json
from datetime import datetime

USER_DATA = dict(
  id = 1, name = 'wxnacy', ts = datetime.now()
)
print(json.dumps(USER_DATA))
Traceback (most recent call last):
 File "/Users/wxnacy/PycharmProjects/study/python/office_module/json_demo/dumps.py", line 74, in <module>
  dumps_encoder()
 File "/Users/wxnacy/PycharmProjects/study/python/office_module/json_demo/dumps.py", line 68, in dumps_encoder
  print(json.dumps(USER_DATA))
 File "/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 231, in dumps
  return _default_encoder.encode(obj)
 File "/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
  chunks = self.iterencode(o, _one_shot=True)
 File "/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
  return _iterencode(o, 0)
 File "/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 180, in default
  o.__class__.__name__)
TypeError: Object of type 'datetime' is not JSON serializable

原因在于 dumps 函数不知道如何处理 datetime 对象,默认情况下 json 模块使用 json.JSONEncoder 类来进行编码,此时我们需要自定义一下编码类。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: wxnacy(wxnacy@gmail.com)

class CustomEncoder(json.JSONEncoder):
  def default(self, x):
    if isinstance(x, datetime):
      return int(x.timestamp())
    return super().default(self, x)

定义编码类 CustomEncoder 并重写实例的 default 函数,对特殊类型进行处理,其余类型继续使用父类的解析。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: wxnacy(wxnacy@gmail.com)

import json
from datetime import datetime

class CustomEncoder(json.JSONEncoder):
  def default(self, x):
    if isinstance(x, datetime):
      return int(x.timestamp())
    return super().default(self, x)

USER_DATA = dict(
  id = 1, name = 'wxnacy', ts = datetime.now()
)
print(json.dumps(USER_DATA, cls=CustomEncoder))
# {"id": 1, "name": "wxnacy", "ts": 1562938926}

最后整合起来,将类使用 cls 参数传入 dumps 函数即可。

使用 CustomEncoder 实例的 encode 函数可以对对象进行转码

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: wxnacy(wxnacy@gmail.com)
print(CustomEncoder().encode(datetime.now()))
# 1562939035

在父类源码中,所有的编码逻辑都在 encode 函数中, default 只负责抛出 TypeError 异常,这就是文章开始报错的出处。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: wxnacy(wxnacy@gmail.com)

def default(self, o):
  """Implement this method in a subclass such that it returns
  a serializable object for ``o``, or calls the base implementation
  (to raise a ``TypeError``).

  For example, to support arbitrary iterators, you could
  implement default like this::

    def default(self, o):
      try:
        iterable = iter(o)
      except TypeError:
        pass
      else:
        return list(iterable)
      # Let the base class default method raise the TypeError
      return JSONEncoder.default(self, o)

  """
  raise TypeError(f'Object of type {o.__class__.__name__} '
          f'is not JSON serializable')

def encode(self, o):
  """Return a JSON string representation of a Python data structure.

  >>> from json.encoder import JSONEncoder
  >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
  '{"foo": ["bar", "baz"]}'

  """
  # This is for extremely simple cases and benchmarks.
  if isinstance(o, str):
    if self.ensure_ascii:
      return encode_basestring_ascii(o)
    else:
      return encode_basestring(o)
  # This doesn't pass the iterator directly to ''.join() because the
  # exceptions aren't as detailed. The list call should be roughly
  # equivalent to the PySequence_Fast that ''.join() would do.
  chunks = self.iterencode(o, _one_shot=True)
  if not isinstance(chunks, (list, tuple)):
    chunks = list(chunks)
  return ''.join(chunks)

单分派装饰器处理对象

CustomEncoder 如果处理的对象种类很多的话,需要写多个 if elif else 来区分,这样并不是不行,但是不够优雅,不够 pythonic

根据对象的类型不同,而做出不同的处理。刚好有个装饰器可以做到这点,它就是单分派函数 functools.singledispatch

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: wxnacy(wxnacy@gmail.com)

from datetime import datetime
from datetime import date
from functools import singledispatch

class CustomEncoder(json.JSONEncoder):
  def default(self, x):
    try:
      return encode(x)
    except TypeError:
      return super().default(self, x)

@singledispatch       # 1
def encode(x):
  raise TypeError('Unencode type')

@encode.register(datetime) # 2
def _(x):
  return int(x.timestamp())

@encode.register(date)
def _(x):
  return x.isoformat()

print(json.dumps(dict(dt = datetime.now(), d = date.today()), cls=CustomEncoder))
# {"dt": 1562940781, "d": "2019-07-12"}

1 使用 @singledispatch 装饰 encode 函数,是他处理默认类型。同时给他添加一个装饰器构造函数变量。
2 `@encode.register () 是一个装饰器构造函数,接收需要处理的对象类型作为参数。用它装饰的函数不需要名字, _` 代替即可。

最后提一点, json 也可以在命令行中使用

$ echo '{"json": "obj"}' | python -m json.tool
{
  "json": "obj"
}

参考链接

json

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python分割文件的常用方法
Nov 01 Python
深入理解Python变量与常量
Jun 02 Python
Python3导入自定义模块的三种方法详解
Apr 13 Python
Python打包方法Pyinstaller的使用
Oct 09 Python
Python 利用切片从列表中取出一部分使用的方法
Feb 01 Python
教你一步步利用python实现贪吃蛇游戏
Jun 27 Python
Python制作词云图代码实例
Sep 09 Python
Jupyter Notebook折叠输出的内容实例
Apr 22 Python
Pycharm中配置远程Docker运行环境的教程图解
Jun 11 Python
详解Python牛顿插值法
May 11 Python
2021年最新用于图像处理的Python库总结
Jun 15 Python
Python 可迭代对象 iterable的具体使用
Aug 07 Python
linux中如何使用python3获取ip地址
Jul 15 #Python
python实现中文文本分句的例子
Jul 15 #Python
Python如何筛选序列中的元素的方法实现
Jul 15 #Python
python内存动态分配过程详解
Jul 15 #Python
python实现动态数组的示例代码
Jul 15 #Python
python移位运算的实现
Jul 15 #Python
python与C、C++混编的四种方式(小结)
Jul 15 #Python
You might like
php获取指定范围内最接近数的方法
2015/06/02 PHP
php生成txt文件实例代码介绍
2016/04/28 PHP
php 如何设置一个严格控制过期时间的session
2017/05/05 PHP
PHP实现对xml的增删改查操作案例分析
2017/05/19 PHP
Laravel框架源码解析之反射的使用详解
2020/05/14 PHP
图片按比例缩放函数
2006/06/26 Javascript
javascript cookie解码函数(兼容ff)
2008/03/17 Javascript
用js判断页面是否加载完成实现代码
2012/12/11 Javascript
将两个div左右并列显示并实现点击标题切换内容
2013/10/22 Javascript
利用javascript实现web页面中指定区域打印
2013/10/30 Javascript
8个超实用的jQuery功能代码分享
2015/01/08 Javascript
跟我学习javascript的prototype,getPrototypeOf和__proto__
2015/11/17 Javascript
jQuery实现网页顶部固定导航效果代码
2015/12/24 Javascript
AngularJS 使用 UI Router 实现表单向导
2016/01/29 Javascript
JavaScript弹出对话框的三种方式
2016/03/23 Javascript
JS跨域交互(jQuery+php)之jsonp使用心得
2016/07/01 Javascript
Vue.js实现模拟微信朋友圈开发demo
2017/04/20 Javascript
详解使用Vue Router导航钩子与Vuex来实现后退状态保存
2017/09/11 Javascript
jQuery实现手机号正则验证输入及自动填充空格功能
2018/01/02 jQuery
Vue单页面应用保证F5强刷不清空数据的解决方案
2018/01/31 Javascript
AjaxUpLoad.js实现文件上传
2018/03/05 Javascript
Vue.js 实现微信公众号菜单编辑器功能(二)
2018/05/08 Javascript
Bootstrap实现模态框效果
2019/09/30 Javascript
Python之csv文件从MySQL数据库导入导出的方法
2018/06/21 Python
Django安装配置mysql的方法步骤
2018/10/15 Python
python实现批量nii文件转换为png图像
2019/07/18 Python
Python3.6实现根据电影名称(支持电视剧名称),获取下载链接的方法
2019/08/26 Python
django有外键关系的两张表如何相互查找
2020/02/10 Python
外贸业务员求职信范文
2013/12/12 职场文书
汉语言文学毕业生自荐信范文
2014/03/24 职场文书
校庆标语集锦
2014/06/25 职场文书
公诉意见书范文
2015/06/05 职场文书
2015年董事长秘书工作总结
2015/07/23 职场文书
导游词之金鞭溪风景区
2019/09/12 职场文书
Oracle配置dblink访问PostgreSQL的操作方法
2022/03/21 PostgreSQL
golang用type-switch判断interface的实际存储类型
2022/04/14 Golang