详解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
深入理解Javascript中的this关键字
Mar 27 Python
使用python装饰器计算函数运行时间的实例
Apr 21 Python
对python append 与浅拷贝的实例讲解
May 04 Python
Python开发最牛逼的IDE——pycharm
Aug 01 Python
python 根据时间来生成唯一的字符串方法
Jan 14 Python
对Python3 解析html的几种操作方式小结
Feb 16 Python
python IDLE 背景以及字体大小的修改方法
Jul 12 Python
Django中使用session保持用户登陆连接的例子
Aug 06 Python
keras模型可视化,层可视化及kernel可视化实例
Jan 24 Python
教你怎么用Python操作MySql数据库
May 31 Python
Python实现Matplotlib,Seaborn动态数据图
May 06 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
全国FM电台频率大全 - 10 江苏省
2020/03/11 无线电
destoon实现底部添加你是第几位访问者的方法
2014/07/15 PHP
PHP+MySQL实现无极限分类栏目的方法
2015/12/23 PHP
详解PHP 7.4 中数组延展操作符语法知识点
2019/07/19 PHP
php探针使用原理和技巧讲解
2019/09/17 PHP
如何取得中文输入的真实长度?
2006/06/24 Javascript
js 刷新页面的代码小结 推荐
2010/04/02 Javascript
JavaScript表格常用操作方法汇总
2015/04/15 Javascript
纯js实现重发验证码按钮倒数功能
2015/04/21 Javascript
javascript如何写热点图
2015/12/08 Javascript
AngularJS入门(用ng-repeat指令实现循环输出
2016/05/05 Javascript
全面解析标签页的切换方式
2016/08/21 Javascript
使用JSON作为函数的参数的优缺点
2016/10/27 Javascript
详解JSON1:使用TSQL查询数据和更新JSON数据
2016/11/21 Javascript
jQuery验证表单格式的使用方法
2017/01/10 Javascript
nodejs6下使用koa2框架实例
2017/05/18 NodeJs
使用vue + less 实现简单换肤功能的示例
2018/02/21 Javascript
解决vue脚手架项目打包后路由视图不显示的问题
2018/09/20 Javascript
vue中的过滤器实例代码详解
2019/06/06 Javascript
vue的keep-alive用法技巧
2019/08/15 Javascript
[04:29]2014DOTA2国际邀请赛 主赛事第三日TOPPLAY
2014/07/21 DOTA
Python提示[Errno 32]Broken pipe导致线程crash错误解决方法
2014/11/19 Python
Python(TensorFlow框架)实现手写数字识别系统的方法
2018/05/29 Python
Python多线程原理与用法详解
2018/08/20 Python
如何编写python的daemon程序
2021/01/07 Python
详解H5 活动页之移动端 REM 布局适配方法
2017/12/07 HTML / CSS
儿媳婚宴答谢词
2014/01/14 职场文书
环保建议书作文
2014/03/12 职场文书
法院授权委托书范文
2014/08/02 职场文书
2014年车间工作总结
2014/11/21 职场文书
小班上学期幼儿评语
2014/12/30 职场文书
2014年个人总结范文
2015/03/09 职场文书
2015年度服装销售工作总结
2015/03/31 职场文书
预备党员入党感想
2015/08/10 职场文书
python迷宫问题深度优先遍历实例
2021/06/20 Python
3050和2060哪个好 性能差多少 差距有多大 谁更有性价比
2022/06/17 数码科技