如何使一个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
当前回答
Kyle Delaney的评论是正确的,所以我尝试使用https://stackoverflow.com/a/15538391/1497139以及https://stackoverflow.com/a/10254820/1497139的改进版本
创建一个“JSONAble”mixin。
因此,要使一个类JSON可序列化使用“JSONAble”作为超类,并调用:
instance.toJSON()
or
instance.asJSON()
对于这两种方法。您还可以使用本文提供的其他方法扩展JSONAble类。
家庭和个人单元测试样本的测试示例结果如下:
toJSOn ():
{
"members": {
"Flintstone,Fred": {
"firstName": "Fred",
"lastName": "Flintstone"
},
"Flintstone,Wilma": {
"firstName": "Wilma",
"lastName": "Flintstone"
}
},
"name": "The Flintstones"
}
asJSOn ():
{'name': 'The Flintstones', 'members': {'Flintstone,Fred': {'firstName': 'Fred', 'lastName': 'Flintstone'}, 'Flintstone,Wilma': {'firstName': 'Wilma', 'lastName': 'Flintstone'}}}
使用家庭和个人样本进行单元测试
def testJsonAble(self):
family=Family("The Flintstones")
family.add(Person("Fred","Flintstone"))
family.add(Person("Wilma","Flintstone"))
json1=family.toJSON()
json2=family.asJSON()
print(json1)
print(json2)
class Family(JSONAble):
def __init__(self,name):
self.name=name
self.members={}
def add(self,person):
self.members[person.lastName+","+person.firstName]=person
class Person(JSONAble):
def __init__(self,firstName,lastName):
self.firstName=firstName;
self.lastName=lastName;
JSONAble .py定义JSONAble mixin
'''
Created on 2020-09-03
@author: wf
'''
import json
class JSONAble(object):
'''
mixin to allow classes to be JSON serializable see
https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable
'''
def __init__(self):
'''
Constructor
'''
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
def getValue(self,v):
if (hasattr(v, "asJSON")):
return v.asJSON()
elif type(v) is dict:
return self.reprDict(v)
elif type(v) is list:
vlist=[]
for vitem in v:
vlist.append(self.getValue(vitem))
return vlist
else:
return v
def reprDict(self,srcDict):
'''
get my dict elements
'''
d = dict()
for a, v in srcDict.items():
d[a]=self.getValue(v)
return d
def asJSON(self):
'''
recursively return my dict elements
'''
return self.reprDict(self.__dict__)
您将发现这些方法现在集成在https://github.com/WolfgangFahl/pyLoDStorage项目中,该项目可在https://pypi.org/project/pylodstorage/上获得
其他回答
Json在它可以打印的对象方面受到限制,而jsonpickle(你可能需要一个PIP安装jsonpickle)在它不能缩进文本方面受到限制。如果你想检查一个你不能改变类的对象的内容,我仍然找不到比:
import json
import jsonpickle
...
print json.dumps(json.loads(jsonpickle.encode(object)), indent=2)
注意:他们仍然不能打印对象方法。
为了给这场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格式的复杂字典。虽然大多数字段携带重要信息,但我们不太关心内置的类对象(例如子进程)。Popen对象)。由于存在这些不可序列化的对象,对json.dumps()的调用会失败。
为了解决这个问题,我构建了一个小函数来转储对象的字符串表示形式,而不是转储对象本身。如果您正在处理的数据结构嵌套太多,您可以指定嵌套的最大级别/深度。
from time import time
def safe_serialize(obj , max_depth = 2):
max_level = max_depth
def _safe_serialize(obj , current_level = 0):
nonlocal max_level
# If it is a list
if isinstance(obj , list):
if current_level >= max_level:
return "[...]"
result = list()
for element in obj:
result.append(_safe_serialize(element , current_level + 1))
return result
# If it is a dict
elif isinstance(obj , dict):
if current_level >= max_level:
return "{...}"
result = dict()
for key , value in obj.items():
result[f"{_safe_serialize(key , current_level + 1)}"] = _safe_serialize(value , current_level + 1)
return result
# If it is an object of builtin class
elif hasattr(obj , "__dict__"):
if hasattr(obj , "__repr__"):
result = f"{obj.__repr__()}_{int(time())}"
else:
try:
result = f"{obj.__class__.__name__}_object_{int(time())}"
except:
result = f"object_{int(time())}"
return result
# If it is anything else
else:
return obj
return _safe_serialize(obj)
由于字典也可以有不可序列化的键,转储它们的类名或对象表示将导致所有键都具有相同的名称,这将抛出错误,因为所有键都需要有唯一的名称,这就是为什么当前时间Since epoch被int(time())附加到对象名称。
可以使用以下具有不同级别/深度的嵌套字典来测试该函数
d = {
"a" : {
"a1" : {
"a11" : {
"a111" : "some_value" ,
"a112" : "some_value" ,
} ,
"a12" : {
"a121" : "some_value" ,
"a122" : "some_value" ,
} ,
} ,
"a2" : {
"a21" : {
"a211" : "some_value" ,
"a212" : "some_value" ,
} ,
"a22" : {
"a221" : "some_value" ,
"a222" : "some_value" ,
} ,
} ,
} ,
"b" : {
"b1" : {
"b11" : {
"b111" : "some_value" ,
"b112" : "some_value" ,
} ,
"b12" : {
"b121" : "some_value" ,
"b122" : "some_value" ,
} ,
} ,
"b2" : {
"b21" : {
"b211" : "some_value" ,
"b212" : "some_value" ,
} ,
"b22" : {
"b221" : "some_value" ,
"b222" : "some_value" ,
} ,
} ,
} ,
"c" : subprocess.Popen("ls -l".split() , stdout = subprocess.PIPE , stderr = subprocess.PIPE) ,
}
执行以下命令将会得到-
print("LEVEL 3")
print(json.dumps(safe_serialize(d , 3) , indent = 4))
print("\n\n\nLEVEL 2")
print(json.dumps(safe_serialize(d , 2) , indent = 4))
print("\n\n\nLEVEL 1")
print(json.dumps(safe_serialize(d , 1) , indent = 4))
结果:
LEVEL 3
{
"a": {
"a1": {
"a11": "{...}",
"a12": "{...}"
},
"a2": {
"a21": "{...}",
"a22": "{...}"
}
},
"b": {
"b1": {
"b11": "{...}",
"b12": "{...}"
},
"b2": {
"b21": "{...}",
"b22": "{...}"
}
},
"c": "<Popen: returncode: None args: ['ls', '-l']>"
}
LEVEL 2
{
"a": {
"a1": "{...}",
"a2": "{...}"
},
"b": {
"b1": "{...}",
"b2": "{...}"
},
"c": "<Popen: returncode: None args: ['ls', '-l']>"
}
LEVEL 1
{
"a": "{...}",
"b": "{...}",
"c": "<Popen: returncode: None args: ['ls', '-l']>"
}
[注意]:仅在不关心内置类对象的序列化时使用此选项。
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))
另一种选择是将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"}'