如何使一个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
当前回答
如果你不介意为它安装一个包,你可以使用json-tricks:
pip install json-tricks
之后,你只需要从json_tricks导入dump(s)而不是json,它通常会工作:
from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)
这将给
{
"__instance_type__": [
"module_name.test_class",
"MyTestCls"
],
"attributes": {
"attr": "val",
"dct_attr": {
"hello": 42
}
}
}
基本上就是这样!
这在一般情况下会很有效。有一些例外,例如,如果特殊的事情发生在__new__中,或者更多的元类魔法正在发生。
显然加载也可以(否则有什么意义):
from json_tricks import loads
json_str = loads(json_str)
这确实假设module_name.test_class。MyTestCls可以导入,并且没有以不兼容的方式进行更改。您将返回一个实例,而不是某个字典或其他东西,它应该是您转储的实例的相同副本。
如果你想自定义一些东西是如何(反)序列化的,你可以添加特殊的方法到你的类,像这样:
class CustomEncodeCls:
def __init__(self):
self.relevant = 42
self.irrelevant = 37
def __json_encode__(self):
# should return primitive, serializable types like dict, list, int, string, float...
return {'relevant': self.relevant}
def __json_decode__(self, **attrs):
# should initialize all properties; note that __init__ is not called implicitly
self.relevant = attrs['relevant']
self.irrelevant = 12
其中仅序列化部分属性参数,作为示例。
作为免费的奖励,你可以获得numpy数组、日期和时间、有序地图的(反)序列化,以及在json中包含注释的能力。
免责声明:我创建了json_tricks,因为我遇到了与您相同的问题。
其他回答
import json
class Foo(object):
def __init__(self):
self.bar = 'baz'
self._qux = 'flub'
def somemethod(self):
pass
def default(instance):
return {k: v
for k, v in vars(instance).items()
if not str(k).startswith('_')}
json_foo = json.dumps(Foo(), default=default)
assert '{"bar": "baz"}' == json_foo
print(json_foo)
如果你正在使用Python3.5+,你可以使用jsons。(PyPi: https://pypi.org/project/jsons/)它将把你的对象(及其所有属性递归地)转换为字典。
import jsons
a_dict = jsons.dump(your_object)
或者如果你想要一个字符串:
a_str = jsons.dumps(your_object)
或者你的类实现了jsons。JsonSerializable:
a_dict = your_object.json
这是我的3美分… 这演示了一个树状python对象的显式json序列化。 注意:如果你真的想要这样的代码,你可以使用twisted FilePath类。
import json, sys, os
class File:
def __init__(self, path):
self.path = path
def isdir(self):
return os.path.isdir(self.path)
def isfile(self):
return os.path.isfile(self.path)
def children(self):
return [File(os.path.join(self.path, f))
for f in os.listdir(self.path)]
def getsize(self):
return os.path.getsize(self.path)
def getModificationTime(self):
return os.path.getmtime(self.path)
def _default(o):
d = {}
d['path'] = o.path
d['isFile'] = o.isfile()
d['isDir'] = o.isdir()
d['mtime'] = int(o.getModificationTime())
d['size'] = o.getsize() if o.isfile() else 0
if o.isdir(): d['children'] = o.children()
return d
folder = os.path.abspath('.')
json.dump(File(folder), sys.stdout, default=_default)
加拉科给出了一个非常简洁的答案。我需要修复一些小的东西,但这是有效的:
Code
# Your custom class
class MyCustom(object):
def __json__(self):
return {
'a': self.a,
'b': self.b,
'__python__': 'mymodule.submodule:MyCustom.from_json',
}
to_json = __json__ # supported by simplejson
@classmethod
def from_json(cls, json):
obj = cls()
obj.a = json['a']
obj.b = json['b']
return obj
# Dumping and loading
import simplejson
obj = MyCustom()
obj.a = 3
obj.b = 4
json = simplejson.dumps(obj, for_json=True)
# Two-step loading
obj2_dict = simplejson.loads(json)
obj2 = MyCustom.from_json(obj2_dict)
# Make sure we have the correct thing
assert isinstance(obj2, MyCustom)
assert obj2.__dict__ == obj.__dict__
注意,加载需要两个步骤。现在是__python__属性 未使用。
这种情况有多普遍?
使用AlJohri的方法,我检查了流行的方法:
序列化(Python -> JSON):
To_json: 266,595 on 2018-06-27 toJSON: 96,307 on 2018-06-27 __json__: 8504 on 2018-06-27 For_json: 6937 on 2018-06-27
反序列化(JSON -> Python):
From_json: 226,101 on 2018-06-27
只需要像这样添加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()。