如何使一个Python类序列化?
class FileItem:
def __init__(self, fname):
self.fname = fname
尝试序列化为JSON:
>>> import json
>>> x = FileItem('/foo/bar')
>>> json.dumps(x)
TypeError: Object of type 'FileItem' is not JSON serializable
如何使一个Python类序列化?
class FileItem:
def __init__(self, fname):
self.fname = fname
尝试序列化为JSON:
>>> import json
>>> x = FileItem('/foo/bar')
>>> json.dumps(x)
TypeError: Object of type 'FileItem' is not JSON serializable
当前回答
我没有看到这里提到串行版本或backcompat,所以我将发布我的解决方案,我已经使用了一点。我可能还有很多东西要学习,特别是Java和Javascript可能比我更成熟,但我要这样做
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
其他回答
除了Onur的答案,你可能想要处理如下的datetime类型。(以便处理:'datetime. time.)Datetime对象没有属性dict异常。)
def datetime_option(value):
if isinstance(value, datetime.date):
return value.timestamp()
else:
return value.__dict__
用法:
def toJSON(self):
return json.dumps(self, default=datetime_option, sort_keys=True, indent=4)
基于Quinten Cabo的回答:
def sterilize(obj):
"""Make an object more ameniable to dumping as json
"""
if type(obj) in (str, float, int, bool, type(None)):
return obj
elif isinstance(obj, dict):
return {k: sterilize(v) for k, v in obj.items()}
list_ret = []
dict_ret = {}
for a in dir(obj):
if a == '__iter__' and callable(obj.__iter__):
list_ret.extend([sterilize(v) for v in obj])
elif a == '__dict__':
dict_ret.update({k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']})
elif a not in ['__doc__', '__module__']:
aval = getattr(obj, a)
if type(aval) in (str, float, int, bool, type(None)):
dict_ret[a] = aval
elif a != '__class__' and a != '__objclass__' and isinstance(aval, type):
dict_ret[a] = sterilize(aval)
if len(list_ret) == 0:
if len(dict_ret) == 0:
return repr(obj)
return dict_ret
else:
if len(dict_ret) == 0:
return list_ret
return (list_ret, dict_ret)
区别在于
Works for any iterable instead of just list and tuple (it works for NumPy arrays, etc.) Works for dynamic types (ones that contain a __dict__). Includes native types float and None so they don't get converted to string. Classes that have __dict__ and members will mostly work (if the __dict__ and member names collide, you will only get one - likely the member) Classes that are lists and have members will look like a tuple of the list and a dictionary Python3 (that isinstance() call may be the only thing that needs changing)
为了给这场11年的大火再添一根柴,我想要一个满足以下条件的解决方案:
只允许使用json.dumps(obj)序列化类FileItem的实例 允许FileItem实例具有属性:FileItem .fname 允许FileItem实例提供给任何库,使用json.dumps(obj)序列化它 不需要将任何其他字段传递给json。转储(如自定义序列化器)
IE:
fileItem = FileItem('filename.ext')
assert json.dumps(fileItem) == '{"fname": "filename.ext"}'
assert fileItem.fname == 'filename.ext'
我的解决方案是:
obj的类是否继承自dict 将每个对象属性映射到底层字典
class FileItem(dict):
def __init__(self, fname):
self['fname'] = fname
#fname property
fname: str = property()
@fname.getter
def fname(self):
return self['fname']
@fname.setter
def fname(self, value: str):
self['fname'] = value
#Repeat for other properties
是的,如果你有很多属性,这有点冗长,但它是JSONSerializable,它的行为像一个对象,你可以把它给任何库,去json.dumps(obj)它。
另一种选择是将JSON转储打包到它自己的类中:
import json
class FileItem:
def __init__(self, fname):
self.fname = fname
def __repr__(self):
return json.dumps(self.__dict__)
或者,更好的是,从JsonSerializable类继承FileItem类:
import json
class JsonSerializable(object):
def toJson(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.toJson()
class FileItem(JsonSerializable):
def __init__(self, fname):
self.fname = fname
测试:
>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'
只需要像这样添加to_json方法到你的类中:
def to_json(self):
return self.message # or how you want it to be serialized
然后将这段代码(来自这个答案)添加到所有内容的顶部:
from json import JSONEncoder
def _default(self, obj):
return getattr(obj.__class__, "to_json", _default.default)(obj)
_default.default = JSONEncoder().default
JSONEncoder.default = _default
这将会在导入json模块时monkey-patch,所以 JSONEncoder.default()自动检查特殊的to_json() 方法,并使用它对找到的对象进行编码。
就像Onur说的,但是这次你不需要更新项目中的每个json.dumps()。