如何使一个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
当前回答
基于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)
其他回答
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)
对于更复杂的类,您可以考虑使用jsonpickle工具:
jsonpickle is a Python library for serialization and deserialization of complex Python objects to and from JSON. The standard Python libraries for encoding Python into JSON, such as the stdlib’s json, simplejson, and demjson, can only handle Python primitives that have a direct JSON equivalent (e.g. dicts, lists, strings, ints, etc.). jsonpickle builds on top of these libraries and allows more complex data structures to be serialized to JSON. jsonpickle is highly configurable and extendable–allowing the user to choose the JSON backend and add additional backends.
(链接到PyPi上的jsonpickle)
我喜欢Onur的答案,但会扩展到包括一个可选的toJSON()方法,用于对象序列化自己:
def dumper(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)
class DObject(json.JSONEncoder):
def delete_not_related_keys(self, _dict):
for key in ["skipkeys", "ensure_ascii", "check_circular", "allow_nan", "sort_keys", "indent"]:
try:
del _dict[key]
except:
continue
def default(self, o):
if hasattr(o, '__dict__'):
my_dict = o.__dict__.copy()
self.delete_not_related_keys(my_dict)
return my_dict
else:
return o
a = DObject()
a.name = 'abdul wahid'
b = DObject()
b.name = a
print(json.dumps(b, cls=DObject))
你们为什么要把事情搞得这么复杂?这里有一个简单的例子:
#!/usr/bin/env python3
import json
from dataclasses import dataclass
@dataclass
class Person:
first: str
last: str
age: int
@property
def __json__(self):
return {
"name": f"{self.first} {self.last}",
"age": self.age
}
john = Person("John", "Doe", 42)
print(json.dumps(john, indent=4, default=lambda x: x.__json__))
这样你也可以序列化嵌套类,因为__json__返回一个python对象而不是字符串。不需要使用JSONEncoder,因为使用简单lambda的默认参数也可以很好地工作。
我使用@property代替了一个简单的函数,因为这样感觉更自然和现代。@dataclass也只是一个例子,它也适用于“普通”类。