详解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之Import 模块
Oct 13 Python
python将文本转换成图片输出的方法
Apr 28 Python
Python函数式编程
Jul 20 Python
启动targetcli时遇到错误解决办法
Oct 26 Python
Python实现线程状态监测简单示例
Mar 28 Python
Python实现多级目录压缩与解压文件的方法
Sep 01 Python
QML使用Python的函数过程解析
Sep 26 Python
Keras实现DenseNet结构操作
Jul 06 Python
Python如何在bool函数中取值
Sep 21 Python
python语言实现贪吃蛇游戏
Nov 13 Python
selenium自动化测试入门实战
Dec 21 Python
Python面向对象编程之类的概念
Nov 01 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
特详细的PHPMYADMIN简明安装教程
2008/08/01 PHP
PHP学习之整理字符串
2011/04/17 PHP
php使用ereg验证文件上传的方法
2014/12/16 PHP
php+redis实现注册、删除、编辑、分页、登录、关注等功能示例
2017/02/15 PHP
得到文本框选中的文字,动态插入文字的js代码
2007/03/07 Javascript
javascript函数库-集合框架
2007/04/27 Javascript
JavaScript中使用Callback控制流程介绍
2015/03/16 Javascript
javascript实现相同事件名称,不同命名空间的调用方法
2015/06/26 Javascript
jQuery EasyUI ProgressBar进度条组件
2017/02/28 Javascript
BootStrap中Table隐藏后显示问题的实现代码
2017/08/31 Javascript
Node.js使用MySQL连接池的方法实例
2018/02/11 Javascript
JavaScript引用类型Function实例详解
2018/08/09 Javascript
JQuery animate动画应用示例
2019/05/14 jQuery
vue使用codemirror的两种用法
2019/08/27 Javascript
基于JavaScript获取base64图片大小
2019/10/18 Javascript
微信小程序的引导页实现代码
2020/06/24 Javascript
利用Vue的v-for和v-bind实现列表颜色切换
2020/07/17 Javascript
vue $mount 和 el的区别说明
2020/09/11 Javascript
[46:55]Ti4 冒泡赛第二轮 LGD vs C9
2014/07/14 DOTA
[03:15]DOTA2-DPC中国联赛1月22日Recap集锦
2021/03/11 DOTA
分析Python读取文件时的路径问题
2018/02/11 Python
解决python3读取Python2存储的pickle文件问题
2018/10/25 Python
微信小程序python用户认证的实现
2019/07/29 Python
python快速编写单行注释多行注释的方法
2019/07/31 Python
Python爬虫抓取指定网页图片代码实例
2020/07/24 Python
使用HTML5和CSS3表单验证功能
2017/05/05 HTML / CSS
CSS3中Animation属性的使用详解
2015/08/06 HTML / CSS
世界领先的艺术图书出版社:TASCHEN
2018/07/23 全球购物
CHRONEXT英国:您的首选奢华腕表目的地
2020/03/30 全球购物
自荐信格式
2013/12/01 职场文书
校园歌手大赛策划书
2014/01/17 职场文书
教师师德师风整改措施
2014/10/24 职场文书
工商局所长四风自我剖析及整改措施
2014/10/26 职场文书
2015年学校食堂工作总结
2015/04/22 职场文书
导游词之安徽醉翁亭
2020/01/10 职场文书
Python 中数组和数字相乘时的注意事项说明
2021/05/10 Python