首先希望大家明白
Python中的序列化首选从来就不是json, 使用json只是为了与其他应用通信.
Python内部通讯, 对象的序列化请使用pickle, 就像你用C/C++, 基本是不会考虑json的,
仅在内部通讯时, 你该用ProtoBuf.
我遇到的一点问题
  Python中免不了也要定义一些类, 自定义的类如果想要序列化, 就是一件麻烦事了.
对于下面这一个类, 如果我们想要格式化某个对象.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | class Inner():def __init__(self):
 self.m_a = 'a'
 self.m_b = 'b'
 
 In [7]: i = Inner()
 
 In [8]: json.dumps(i)
 ---------------------------------------------------------------------------
 TypeError                                 Traceback (most recent call last)
 
 | 
这样我们就会考虑给这个Inner类增加一个函数, 变成类似下面这个样子.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | class Inner():""" 一个类 """
 def __init__(self):
 self.m_a = 'a'
 self.m_b = 'b'
 
 def to_json(self):
 return json.dumps({
 'm_a': self.m_a,
 'm_b': self.m_b,
 })
 
 
 In [19]: i.to_json()
 Out[19]: '{"m_a": "a", "m_b": "b"}'
 
 | 
然后, 你觉得世界就变得正常了, 一切的一切就满足了吗.
愿望是极好的, 但是… 正常的生活不是由一个类组成的.
上面的那个类名叫Inner, 那会不会有一个类叫Outer包含它呢, 好像很可能会有吧.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | class Outer():
 def __init__(self):
 self.m_i = Inner()
 self.m_c = 'c'
 
 In [21]: o = Outer()
 
 In [22]: json.dumps(o)
 ---------------------------------------------------------------------------
 TypeError                                 Traceback (most recent call last)
 
 | 
于是你决定也向Outer中再加一个函数, 好像像下面这样也能达到效果.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | class Outer():""" 一个类 """
 def __init__(self):
 self.m_i = Inner()
 self.m_c = 'c'
 
 def to_json(self):
 return json.dumps({
 'm_i': {
 'm_a': self.m_i.m_a,
 'm_b': self.m_i.m_b,
 },
 'm_c': self.m_c
 })
 
 In [26]: o.to_json()
 Out[26]: '{"m_i": {"m_a": "a", "m_b": "b"}, "m_c": "c"}'
 
 | 
世界好像也不止有两个类吧.
  现在, 我们遇到了一个问题, 再出现类似的情况怎么办, 我们要一层一层的去包裹.
而且是纯手动, 你们能想想这个过程有多么的特别吗, 而且每个对象需要调用
to_json这一类函数, 的确很难.
  这个时候, 你该考虑Google, StackOverflow了, 也许有人曾经遇到过这样的问题,
我搜索的是python json dumps custome object这样的一串关键字.
How to make a class JSON serializable是第一个内容, 里面介绍了一些方法.
例如:
| 12
 3
 4
 5
 6
 7
 8
 
 | class CustomJsonEncoder(json.JSONEncoder):  def default(self, obj):
 if isinstance(obj, Outer):
 return {'m_c': obj.m_c}
 return json.JSONEncoder.default(self, obj)
 
 In [32]: json.dumps(o, cls=CustomJsonEncoder)
 Out[32]: '{"m_c": "c"}'
 
 | 
借助这个方式, 我们可以更新原来的Outer与Inner类, 再与CustomJsonEncoder结合
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | class Inner():""" 一个类 """
 def __init__(self):
 self.m_a = 'a'
 self.m_b = 'b'
 
 def to_json(self):
 return {
 'm_a': self.m_a,
 'm_b': self.m_b,
 }
 
 class Outer():
 """ 一个类 """
 def __init__(self):
 self.m_i = Inner()
 self.m_c = 'c'
 
 def to_json(self):
 return {
 'm_i': self.m_i,
 'm_c': self.m_c
 }
 
 class CustomJsonEncoder(json.JSONEncoder):
 def default(self, obj):
 if getattr(obj, "to_json", None):
 return obj.to_json()
 return json.JSONEncoder.default(self, obj)
 
 In [35]: o = Outer()
 
 In [36]: json.dumps(o, cls=CustomJsonEncoder)
 Out[36]: '{"m_i": {"m_a": "a", "m_b": "b"}, "m_c": "c"}'
 
 | 
你别说, 还是挺成功的. 这样一来, 自己的定义的类也可以实现to_json这个方法,
缺点就是每次json.dumps的时候需要加上后面这一串了cls=CustomJsonEncoder
能不能再优雅一点
  json.dumps(o, cls=CustomJsonEncoder)这句话真的挺长的, 如果我们能简化
一点就好了, 还像原来那样json.dumps(o)多好.
  我是比较幸运的, 又找到了How to make a class JSON serializable. 里面提供
了一种思路, 由于json.dumps时, 默认的JSONEncoder是可以被我们找到的.
既然如此, 将它替换也是可能的, 这是原文中的代码(原文中有其他方法, 也可以去读读看).
| 12
 3
 4
 5
 6
 7
 
 | from json import JSONEncoder
 def _default(self, obj):
 return getattr(obj.__class__, "to_json", _default.default)(obj)
 
 _default.default = JSONEncoder().default
 JSONEncoder.default = _default
 
 | 
我在自己的项目中, 换用了另外一种形式, 我希望原始的default最先被调用, 而后才是
我们自定义的to_json, 另外对于项目中的Decimal对象, 原始的函数似乎也不能用,
我就只能单独放出来了.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | def _default(self, obj):""" 以补丁的形式重写了json.JSONEncoder.default函数
 避免在每个json.dumps处修改
 """
 if isinstance(obj, Decimal):
 return float(obj)
 
 try:
 orig_default(self, obj)
 except:
 return getattr(obj.__class__, "to_json")(obj)
 
 json.JSONEncoder.default = _default
 
 | 
** << Dive Into Python >> **
在Python中没有常量, 如果你试图努力的话什么都可以改变.
这一点满足Python的核心原则之一: 坏的行为应该被克服而不是被取缔.
你们认为这个方法怎么样?
好处是, 你可以将cls=CustomJsonEncoder去掉了.
而随之而来的坏处就是, 你的程序修改了原始的JSONEncoder中的default, 你看我在我
上面的程序中, 尽最大努力让程序先调用原来的default, 即便这样, Decimal对象在
序列化时仍然出现了问题, 不得不单独提出来.
另外一个问题就是, 你每定义一个类, 就意味着你需要实现一个to_json方法,
这种隐式的约定, 永远是可能出现问题的地方.
所幸, 我们平时使用也应该使用的是pickle, 而不是json.
json应该只在这一种情况下使用: 你想要与其他语言的程序通讯时.