详解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中删除文件的程序代码
Mar 13 Python
Python正则表达式使用经典实例
Jun 21 Python
Python使用回溯法子集树模板解决爬楼梯问题示例
Sep 08 Python
pandas.loc 选取指定列进行操作的实例
May 18 Python
pandas 选择某几列的方法
Jul 03 Python
Python在cmd上打印彩色文字实现过程详解
Aug 07 Python
python3 深浅copy对比详解
Aug 12 Python
python基础 range的用法解析
Aug 23 Python
python3中使用__slots__限定实例属性操作分析
Feb 14 Python
Python3爬虫关于识别检验滑动验证码的实例
Jul 30 Python
利用python清除移动硬盘中的临时文件
Oct 28 Python
python制作抽奖程序代码详解
Jan 15 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中base_convert()进制数字转换函数实例
2014/11/20 PHP
php中fsockopen用法实例
2015/01/05 PHP
PHP在同一域名下两个不同的项目做独立登录机制详解
2017/09/22 PHP
js focus不起作用的解决方法(主要是因为dom元素是否加载完成)
2010/11/05 Javascript
JavaScript获取图片的原始尺寸以宽度为例
2014/05/04 Javascript
jQuery提交多个表单的小技巧
2014/07/27 Javascript
node.js中的events.EventEmitter.listenerCount方法使用说明
2014/12/08 Javascript
js鼠标悬浮出现遮罩层的方法
2015/01/28 Javascript
学习JavaScript设计模式(策略模式)
2015/11/26 Javascript
详解关于react-redux中的connect用法介绍及原理解析
2017/09/11 Javascript
详解Angular调试技巧之报错404(not found)
2018/01/31 Javascript
弱类型语言javascript中 a,b 的运算实例小结
2019/08/07 Javascript
json.stringify()与json.parse()的区别以及用处
2021/01/25 Javascript
[01:15:36]加油刀塔第二期网络版
2014/08/09 DOTA
Python使用BeautifulSoup库解析HTML基本使用教程
2016/03/31 Python
Python 多核并行计算的示例代码
2017/11/07 Python
基于Django URL传参 FORM表单传数据 get post的用法实例
2018/05/28 Python
python实现列表的排序方法分享
2019/07/01 Python
Django命名URL和反向解析URL实现解析
2019/08/09 Python
python实现批量文件重命名
2019/10/31 Python
Django 构建模板form表单的两种方法
2020/06/14 Python
django创建css文件夹的具体方法
2020/07/31 Python
python缩进长度是否统一
2020/08/02 Python
Python 保存加载mat格式文件的示例代码
2020/08/04 Python
5 分钟读懂Python 中的 Hook 钩子函数
2020/12/09 Python
纯CSS实现颜色渐变效果(包含环形渐变、线性渐变、彩虹效果等)
2014/05/07 HTML / CSS
纯CSS3实现圆圈动态发光特效动画的示例代码
2021/03/08 HTML / CSS
文件中有一组整数,要求排序后输出到另一个文件中
2012/01/04 面试题
.NET程序员的几道面试题
2012/06/01 面试题
优秀大学生推荐信范文
2013/11/28 职场文书
高一历史教学反思
2014/01/13 职场文书
美术兴趣小组活动总结
2014/07/07 职场文书
2015年售后服务工作总结
2015/04/25 职场文书
2016中秋晚会开幕词
2016/03/03 职场文书
关于Spring配置文件加载方式变化引发的异常详解
2022/01/18 Java/Android
大脑的记忆过程在做数据压缩,不同图形也有共同的记忆格式
2022/04/29 数码科技