如何使一个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
这里有一个简单的解决方案来实现一个简单的功能:
.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"
}
o.__dict___
。试试你自己的例子:class MyObject():
def __init__(self):
self.prop = 1
j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
- Onur Yıldırımdatetime.datetime
实例。它会抛出以下错误:'datetime.datetime' object has no attribute '__dict__'
。 - Bruno Fingerjson.dumps(me)
没有调用Object
的toJSON
方法)。 - cglacet你有对期望输出的想法吗?比如说,这样行吗?
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
在这种情况下,你可以简单地调用 json.dumps(f.__dict__)
。JSONEncoder
并实现自己的自定义序列化。>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
然后你将这个类作为cls
关键字参数传递给json.dumps()
方法:
json.dumps(cls=MyEncoder)
如果您还想进行解码,则需要向 JSONDecoder
类提供一个自定义的 object_hook
。例如:
>>> def from_json(json_object):
if 'fname' in json_object:
return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>>
__dict__
并不适用于所有情况。如果对象实例化后未设置属性,则__dict__
可能不会完全填充。在上面的示例中,您没有问题,但是如果您有类属性也想进行编码,则这些属性将不会在__dict__
中列出,除非它们在类的__init__
调用或在对象实例化后通过某种其他方式进行了修改。 - Kris Hardyfrom_json()
函数应该加上else: return json_object
语句,以便处理普通对象。 - jogojapan__slots__
,那么__dict__
也无法使用。 - badpJSONEncoder
,如上所述,创建一个自定义协议,例如检查是否存在 __json_serializable__
方法并调用它来获取对象的 JSON 可序列化表示。这与其他 Python 模式保持一致,例如 __getitem__
、__str__
、__eq__
和 __len__
。 - jpmc26__dict__
也不能递归地工作,例如,如果您对象的属性是另一个对象。 - Neelimport jsonpickle
json_string = jsonpickle.encode(obj)
recreated_obj = jsonpickle.decode(json_string)
jsonpickle
对象。此外,它无法解码包含 Pandas 数据帧的字典字典。 - user5359531obj = jsonpickle.decode(file.read())
和file.write(jsonpickle.encode(obj))
。这些代码可用于将JSON格式的数据解码为Python对象,并将Python对象编码为JSON格式并写入文件中。 - Kilian Batzner大多数答案都涉及更改对 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
如果你的类只是基本数据表示,那么这将起作用,对于更棘手的事情,你总是可以在调用dict.__init__()
时显式设置键。
这能够起作用是因为json.dumps()
通过一个相当不符合Python风格的isinstance(value, dict)
检查对象是否属于几个已知类型之一 - 因此,如果你真的不想从dict
继承,使用__class__
和其他一些方法也是可能的。
super().__init__(self.elements)
。 - cglacetjson.dumps
的代码时。 - andyhasit正如其他答案中提到的那样,您可以将一个函数传递给json.dumps
来将默认不支持的对象转换为支持的类型。令人惊讶的是,它们中没有任何一种提到最简单的情况,即使用内置函数vars
将对象转换为包含其所有属性的字典:
json.dumps(obj, default=vars)
请注意,这仅涵盖基本情况。如果您需要针对某些类型进行更具体的序列化(例如,排除某些属性或对象没有__dict__
属性),则需要使用自定义函数或像其他答案中描述的JSONEncoder
。
vars() argument must have __dict__ attribute
的方法? - undefined只需要像这样在你的类中添加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
JSONEncoder.default()
会自动检查特殊的to_json()
方法,并在找到时使用它来编码对象。json.dumps()
。TheObject.to_json = my_serializer
即可。 - Yongwei Wu
_fallback = json._default_encoder.default
json._default_encoder.default = lambda obj: getattr(obj.__class__, "to_json", _fallback)(obj)
- Kjirjson
模块与您的类一起使用也就是解决:json.dumps({ "thing": YOUR_CLASS() })
toJSON
那样向类添加方法,也无法将类注册到内置的json模块中。当执行json.dumps([1,2, your_obj])
这样的代码时,Python不会检查查找表或对象方法。def __json__(self)
方法。json.dumps
以检查__json__
方法(会影响到任何地方,甚至是导入json的pip模块)。pip install json-fix
(Fancy John's answer的扩展+打包版本,感谢@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]'
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"
_
所有其他答案似乎都是关于“序列化自定义对象的最佳实践/方法”的内容。json.dumps
有点过于激进,但在我看来,这显然是最好的解决方案。 - rjhBigInt.from_json(a_str)
这样做,使用你知道应该是BigInt的字符串。 - Jeff Hykin我喜欢Onur的回答,但我会扩展以包括一个可选的toJSON()
方法,使对象能够序列化自己:
def dumper(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)
json.dumps
和引入自定义处理之间取得最佳平衡的方法。谢谢! - Daniel Buckmasterif 'toJSON' in obj.__attrs__():
替代 try-catch
来避免悄无声息的失败(如果由于某些原因而导致 toJSON() 失败,而不是因为函数本身不存在)。这种失败可能会导致数据损坏。 - thclarkAttributeError
。我的翻译如下:This error should be caught explicitly as an AttributeError
. - juanpa.arrivillagaobj.toJSON()
内部引发了 AttributeError
,那该怎么办? - artmjsons
。(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
jsons
库与dataclasses混合使用。目前为止,对我来说一切都很好! - Rulukjson.dumps(obj)
然后 json.loads(obj)
。 - RustyShackleford另一种选择是将 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"}'
__json__encode__
/ __json_decode__
来更改它(揭示:我制作了最后一个)。 - Markjson.dumps(f)
将失败。这不是被要求的。 - omni
import jsons
查看下面的答案 - 它可以正常工作。 - tswaehn.to_dict()
函数或者其他类似的东西,在对象传递给尝试序列化它的模块之前调用它。 - Felix B.json.dumps
,但是所有的回答,包括获得奖励的回答,都涉及创建一个自定义编码器,这完全回避了问题的核心。 - Mike