如何使一个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
当前回答
任何人都想在没有外部库的情况下使用基本转换,这只是如何使用以下方式覆盖自定义类的__iter__ & __str__函数。
class JSONCustomEncoder(json.JSONEncoder):
def default(self, obj):
return obj.__dict__
class Student:
def __init__(self, name: str, slug: str):
self.name = name
self.age = age
def __iter__(self):
yield from {
"name": self.name,
"age": self.age,
}.items()
def __str__(self):
return json.dumps(
self.__dict__, cls=JSONCustomEncoder, ensure_ascii=False
)
通过在dict()中进行包装来使用该对象,从而保留数据。
s = Student("aman", 24)
dict(s)
其他回答
下面是一个简单功能的简单解决方案:
.toJSON()方法
实现一个序列化器方法,而不是一个JSON可序列化类:
import json
class Object:
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
所以你只需调用它来序列化:
me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"
print(me.toJSON())
将输出:
{
"age": 35,
"dog": {
"name": "Apollo"
},
"name": "Onur"
}
大多数答案都涉及更改对json.dumps()的调用,这并不总是可能的或可取的(例如,它可能发生在框架组件内部)。
如果你希望能够按原样调用json.dumps(obj),那么一个简单的解决方案是从dict继承:
class FileItem(dict):
def __init__(self, fname):
dict.__init__(self, fname=fname)
f = FileItem('tasks.txt')
json.dumps(f) #No need to change anything here
如果你的类只是基本的数据表示,这是可行的,对于更棘手的事情,你总是可以显式地设置键。
要添加另一个选项:您可以使用attrs包和asdict方法。
class ObjectEncoder(JSONEncoder):
def default(self, o):
return attr.asdict(o)
json.dumps(objects, cls=ObjectEncoder)
然后再转换回去
def from_json(o):
if '_obj_name' in o:
type_ = o['_obj_name']
del o['_obj_name']
return globals()[type_](**o)
else:
return o
data = JSONDecoder(object_hook=from_json).decode(data)
类看起来像这样
@attr.s
class Foo(object):
x = attr.ib()
_obj_name = attr.ib(init=False, default='Foo')
TLDR:复制-粘贴下面的选项1或选项2
真正的/完整的答案:让Pythons json模块与你的类一起工作
AKA,求解:json。dump ({"thing": YOUR_CLASS()})
解释:
Yes, a good reliable solution exists No, there is no python "official" solution By official solution, I mean there is no way (as of 2023) to add a method to your class (like toJSON in JavaScript) and/or no way to register your class with the built-in json module. When something like json.dumps([1,2, your_obj]) is executed, python doesn't check a lookup table or object method. I'm not sure why other answers don't explain this The closest official approach is probably andyhasit's answer which is to inherit from a dictionary. However, inheriting from a dictionary doesn't work very well for many custom classes like AdvancedDateTime, or pytorch tensors. The ideal workaround is this: Mutate json.dumps (affects everywhere, even pip modules that import json) Add def __json__(self) method to your class
选项1:让一个模块来做补丁
PIP安装json-fix (扩展+包装版FancyJohn的回答,谢谢@FancyJohn)
your_class_definition.py
import json_fix
class YOUR_CLASS:
def __json__(self):
# YOUR CUSTOM CODE HERE
# you probably just want to do:
# return self.__dict__
return "a built-in object that is naturally json-able"
这是它。
使用示例:
from your_class_definition import YOUR_CLASS
import json
json.dumps([1,2, YOUR_CLASS()], indent=0)
# '[\n1,\n2,\n"a built-in object that is naturally json-able"\n]'
生成json。dump适用于Numpy数组,Pandas DataFrames和其他第三方对象,请参阅模块(只有大约2行代码,但需要解释)。
它是如何工作的?嗯…
选项2:补丁json。把你自己
注意:这种方法是简化的,它在已知的edgcase上失败(例如:如果你的自定义类继承了dict或其他内置类),并且它错过了控制外部类的json行为(numpy数组,datetime, dataframes,张量等)。
some_file_thats_imported_before_your_class_definitions.py
# Step: 1
# create the patch
from json import JSONEncoder
def wrapped_default(self, obj):
return getattr(obj.__class__, "__json__", wrapped_default.default)(obj)
wrapped_default.default = JSONEncoder().default
# apply the patch
JSONEncoder.original_default = JSONEncoder.default
JSONEncoder.default = wrapped_default
your_class_definition.py
# Step 2
class YOUR_CLASS:
def __json__(self, **options):
# YOUR CUSTOM CODE HERE
# you probably just want to do:
# return self.__dict__
return "a built-in object that is natually json-able"
_
其他答案似乎都是“序列化自定义对象的最佳实践/方法”
在这里的文档中已经介绍过了(搜索“complex”可以找到编码复数的例子)
为了给这场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)它。