详解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中unittest用法实例
Sep 25 Python
python正常时间和unix时间戳相互转换的方法
Apr 23 Python
Python中几个比较常见的名词解释
Jul 04 Python
python中logging库的使用总结
Oct 18 Python
Python使用re模块正则提取字符串中括号内的内容示例
Jun 01 Python
python关于矩阵重复赋值覆盖问题的解决方法
Jul 19 Python
Python 使用多属性来进行排序
Sep 01 Python
python flask中动态URL规则详解
Nov 22 Python
Python flask框架实现浏览器点击自定义跳转页面
Jun 04 Python
Python调用.net动态库实现过程解析
Jun 05 Python
详细分析Python可变对象和不可变对象
Jul 09 Python
基于Python实现射击小游戏的制作
Apr 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
PHP中大于2038年时间戳的问题处理方案
2015/03/03 PHP
PHP性能测试工具xhprof安装与使用方法详解
2018/04/29 PHP
jquery ui对话框实例代码
2013/05/10 Javascript
js 获取元素在页面上的偏移量的方法汇总
2015/04/13 Javascript
jquery利用拖拽方式在图片上添加热链接
2015/11/24 Javascript
关于在Servelet中如何获取当前时间的操作方法
2016/06/28 Javascript
详解JavaScript的内置对象
2016/12/07 Javascript
微信小程序 支付功能(前端)的实现
2017/05/24 Javascript
JS中定位 position 的使用实例代码
2017/08/06 Javascript
jQuery的时间datetime控件在AngularJs中的使用实例(分享)
2017/08/17 jQuery
jQuery访问浏览器本地存储cookie、localStorage和sessionStorage的基本用法
2017/10/20 jQuery
解决vue页面DOM操作不生效的问题
2018/03/17 Javascript
javascript深拷贝、浅拷贝和循环引用深入理解
2018/05/27 Javascript
详解vue 在移动端体验上的优化解决方案
2019/05/20 Javascript
JS继承定义与使用方法简单示例
2020/02/19 Javascript
python通过socket查询whois的方法
2015/07/18 Python
Python爬取京东的商品分类与链接
2016/08/26 Python
wxPython实现画图板
2020/08/27 Python
6行Python代码实现进度条效果(Progress、tqdm、alive-progress​​​​​​​和PySimpleGUI库)
2020/01/06 Python
Django之form组件自动校验数据实现
2020/01/14 Python
Python3 元组tuple入门基础
2020/02/09 Python
Python多重继承之菱形继承的实例详解
2020/02/12 Python
python异常处理之try finally不报错的原因
2020/05/18 Python
H5页面适配iPhoneX(就是那么简单)
2019/12/02 HTML / CSS
澳大利亚百货公司:David Jones
2018/02/08 全球购物
美国现代家具网站:Design Within Reach
2018/07/19 全球购物
高校自主招生自荐信
2013/12/09 职场文书
学生打架检讨书1000字
2014/01/16 职场文书
12岁生日感言
2014/01/21 职场文书
法律专业大学生职业生涯规划书:向目标一步步迈进
2014/09/22 职场文书
2014年英语工作总结
2014/12/20 职场文书
第一书记观后感
2015/06/08 职场文书
运动员入场词
2015/07/18 职场文书
2019个人工作计划书的格式及范文!
2019/07/04 职场文书
Java使用jmeter进行压力测试
2021/07/09 Java/Android
拙作再改《我的收音机情缘》
2022/04/05 无线电