详解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入门_浅谈字符串的分片与索引、字符串的方法
May 16 Python
Windows环境下python环境安装使用图文教程
Mar 13 Python
Django实现分页功能
Jul 02 Python
Python DataFrame.groupby()聚合函数,分组级运算
Sep 18 Python
基于python实现KNN分类算法
Apr 23 Python
python粘包问题及socket套接字编程详解
Jun 29 Python
Python绘制股票移动均线的实例
Aug 24 Python
python logging日志模块原理及操作解析
Oct 12 Python
Flask框架搭建虚拟环境的步骤分析
Dec 21 Python
pytorch cuda上tensor的定义 以及减少cpu的操作详解
Jun 23 Python
Ubuntu权限不足无法创建文件夹解决方案
Nov 14 Python
基于tensorflow __init__、build 和call的使用小结
Feb 26 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
默默简单的写了一个模板引擎
2007/01/02 PHP
php数组函数序列之array_search()- 按元素值返回键名
2011/11/04 PHP
详解PHP变量传值赋值和引用赋值变量销毁
2019/03/23 PHP
php使用mysqli和pdo扩展,测试对比mysql数据库的执行效率完整示例
2019/05/09 PHP
JQUERY THICKBOX弹出层插件
2008/08/30 Javascript
eval与window.eval的差别分析
2011/03/17 Javascript
各种常用的JS函数整理
2013/10/25 Javascript
JavaScript基础知识学习笔记
2014/12/02 Javascript
node.js中的forEach()是同步还是异步呢
2015/01/29 Javascript
jQuery控制网页打印指定区域的方法
2015/04/07 Javascript
jQuery获取浏览器类型和版本号的方法
2016/07/05 Javascript
使用jQuery调用XML实现无刷新即时聊天
2016/08/07 Javascript
微信小程序 animation API详解及实例代码
2016/10/08 Javascript
JQuery实现文字无缝滚动效果示例代码(Marquee插件)
2017/03/07 Javascript
解决Webpack 热部署检测不到文件变化的问题
2018/02/22 Javascript
jquery点击回车键实现登录效果并默认焦点的方法
2018/03/09 jQuery
json对象及数组键值的深度大小写转换问题详解
2018/03/30 Javascript
angularJs提交文本框数据到后台的方法
2018/10/08 Javascript
在vue中使用G2图表的示例代码
2019/03/19 Javascript
VSCode使用之Vue工程配置eslint
2019/04/30 Javascript
基于layui内置模块(element常用元素的操作)
2019/09/20 Javascript
微信小程序实现翻牌抽奖动画
2020/09/21 Javascript
Python解决鸡兔同笼问题的方法
2014/12/20 Python
python多线程之事件Event的使用详解
2018/04/27 Python
对Tensorflow中的矩阵运算函数详解
2018/07/27 Python
Django框架首页和登录页分离操作示例
2019/05/28 Python
浅谈如何使用python抓取网页中的动态数据实现
2020/08/17 Python
来自全球大都市的高级街头服饰:Pegador
2018/01/03 全球购物
造型师求职自荐信
2013/09/27 职场文书
自荐信模版
2013/10/24 职场文书
初一家长会邀请函
2014/01/31 职场文书
转让协议书
2015/01/27 职场文书
工会经费申请报告
2015/05/15 职场文书
关于感恩的素材句子(38句)
2019/11/11 职场文书
python中sys模块的介绍与实例
2021/04/17 Python
html解决浏览器记住密码输入框的问题
2023/05/07 HTML / CSS