详解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 条件判断的缩写方法
Sep 06 Python
Python线程详解
Jun 24 Python
CentOS 7下Python 2.7升级至Python3.6.1的实战教程
Jul 06 Python
Python使用三种方法实现PCA算法
Dec 12 Python
Python检测网络延迟的代码
May 15 Python
Python文件读写保存操作的示例代码
Sep 14 Python
Python global全局变量函数详解
Sep 18 Python
Python实现简单查找最长子串功能示例
Feb 26 Python
Python实现病毒仿真器的方法示例(附demo)
Feb 19 Python
python中的selenium安装的步骤(浏览器自动化测试框架)
Mar 17 Python
Python利用pip安装tar.gz格式的离线资源包
Sep 14 Python
Keras在mnist上的CNN实践,并且自定义loss函数曲线图操作
May 25 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
初识Laravel
2014/10/30 PHP
thinkphp框架表单数组实现图片批量上传功能示例
2020/04/04 PHP
document.compatMode介绍
2009/05/21 Javascript
js 键盘记录实现(兼容FireFox和IE)
2010/02/07 Javascript
javascript深入理解js闭包
2010/07/03 Javascript
jQuery实现自定义下拉列表
2015/01/05 Javascript
第十篇BootStrap轮播插件使用详解
2016/06/21 Javascript
AngularJS基础 ng-cloak 指令简单示例
2016/08/01 Javascript
jQuery视差滚动效果网页实现方法经验总结
2016/09/29 Javascript
Bootstrap的modal拖动效果
2016/12/25 Javascript
原生javascript实现图片放大镜效果
2017/01/18 Javascript
微信小程序使用蓝牙小插件
2019/09/23 Javascript
[26:52]LGD vs EG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
Python中Collections模块的Counter容器类使用教程
2016/05/31 Python
python发送邮件功能实现代码
2016/07/15 Python
Python PyQt5标准对话框用法示例
2017/08/23 Python
JS设计模式之责任链模式实例详解
2018/02/03 Python
详解Django+Uwsgi+Nginx 实现生产环境部署
2018/11/06 Python
Python 图像对比度增强的几种方法(小结)
2019/09/25 Python
如何在mac版pycharm选择python版本
2020/07/21 Python
简单了解python关键字global nonlocal区别
2020/09/21 Python
python 用opencv实现霍夫线变换
2020/11/27 Python
python爬取微博评论的实例讲解
2021/01/15 Python
BRASTY捷克:购买香水、化妆品、手袋和手表
2017/07/12 全球购物
优秀志愿者事迹材料
2014/02/03 职场文书
军训自我鉴定怎么写
2014/02/13 职场文书
自我鉴定总结
2014/03/24 职场文书
精神文明单位申报材料
2014/05/02 职场文书
个人工作主要事迹
2014/05/08 职场文书
关于学习的演讲稿
2014/05/10 职场文书
文明市民先进事迹
2014/05/15 职场文书
2014年居委会工作总结
2014/12/09 职场文书
我们的节日端午节活动总结
2015/02/11 职场文书
新农村建设指导员工作总结
2015/08/13 职场文书
instantclient客户端 连接oracle数据库
2022/04/26 Oracle
Python面试不修改数组找出重复的数字
2022/05/20 Python