让类实现JSON序列化的方法
技术背景
在Python开发中,JSON(JavaScript Object Notation)是一种常用的数据交换格式。然而,Python中的自定义类对象不能直接被json
模块序列化。因此,我们需要找到一些方法来实现自定义类的JSON序列化,以便在不同系统之间交换数据。
实现步骤
1. .toJSON()
方法
实现一个序列化方法,而不是直接让类成为JSON可序列化的。示例代码如下:
1 2 3 4 5 6 7 8 9
| import json
class Object: def toJSON(self): return json.dumps( self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
|
使用示例:
1 2 3 4 5 6 7
| me = Object() me.name = "Onur" me.age = 35 me.dog = Object() me.dog.name = "Apollo"
print(me.toJSON())
|
2. 使用 orjson
库
orjson
是一个功能强大的JSON处理库,可以直接使用。示例代码如下:
1 2 3 4 5 6 7 8 9 10
| import orjson
class MyClass: def __init__(self, name, age): self.name = name self.age = age
obj = MyClass("John", 30) json_data = orjson.dumps(obj.__dict__) print(json_data)
|
3. 自定义 JSONEncoder
通过继承 JSONEncoder
类,实现自定义的序列化逻辑。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import json
class ObjectEncoder(json.JSONEncoder): def default(self, obj): if hasattr(obj, "__dict__"): return obj.__dict__ return json.JSONEncoder.default(self, obj)
class MyClass: def __init__(self, name, age): self.name = name self.age = age
obj = MyClass("John", 30) json_data = json.dumps(obj, cls=ObjectEncoder) print(json_data)
|
4. 使用 jsonpickle
库
jsonpickle
可以处理更复杂的Python对象的序列化和反序列化。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import jsonpickle
class MyClass: def __init__(self, name, age): self.name = name self.age = age
obj = MyClass("John", 30) json_string = jsonpickle.encode(obj) print(json_string)
recreated_obj = jsonpickle.decode(json_string) print(recreated_obj.name, recreated_obj.age)
|
5. 继承 dict
类
如果类只是简单的数据表示,可以继承 dict
类。示例代码如下:
1 2 3 4 5 6 7 8 9
| import json
class FileItem(dict): def __init__(self, fname): dict.__init__(self, fname=fname)
f = FileItem('tasks.txt') json_data = json.dumps(f) print(json_data)
|
6. 使用 jsons
库
jsons
可以将对象(及其所有属性)递归地转换为字典。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import jsons
class MyClass: def __init__(self, name, age): self.name = name self.age = age
obj = MyClass("John", 30) a_dict = jsons.dump(obj) print(a_dict)
a_str = jsons.dumps(obj) print(a_str)
|
7. 包装JSON转储逻辑
将JSON转储逻辑封装在类中。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11
| import json
class FileItem: def __init__(self, fname: str) -> None: self.fname = fname
def __repr__(self) -> str: return json.dumps(self.__dict__)
f = FileItem('/foo/bar') print(f)
|
8. 自定义序列化函数
使用自定义函数处理序列化。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import json
def dumper(obj): try: return obj.toJSON() except: return obj.__dict__
class MyClass: def __init__(self, name, age): self.name = name self.age = age
obj = MyClass("John", 30) json_data = json.dumps(obj, default=dumper, indent=2) print(json_data)
|
9. 使用 json-tricks
库
json-tricks
可以处理复杂对象的序列化和反序列化。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from json_tricks import dumps, loads
class MyClass: def __init__(self, relevant): self.relevant = relevant self.irrelevant = 37
def __json_encode__(self): return {'relevant': self.relevant}
def __json_decode__(self, **attrs): self.relevant = attrs['relevant'] self.irrelevant = 12
obj = MyClass(42) json_str = dumps(obj, indent=4) print(json_str)
recreated_obj = loads(json_str) print(recreated_obj.relevant)
|
10. 自定义序列化方法
通过定义 __json__
方法实现自定义序列化。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import json
def _default(self, obj): return getattr(obj.__class__, "__json__", _default.default)(obj)
_default.default = json.JSONEncoder().default json.JSONEncoder.default = _default
class MyClass: def __json__(self): return {'name': 'John', 'age': 30}
obj = MyClass() json_data = json.dumps(obj) print(json_data)
|
核心代码
以下是一个综合示例,展示了如何使用自定义 JSONEncoder
处理嵌套对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import json import inspect
class ObjectEncoder(json.JSONEncoder): def default(self, obj): if hasattr(obj, "to_json"): return self.default(obj.to_json()) elif hasattr(obj, "__dict__"): d = dict( (key, value) for key, value in inspect.getmembers(obj) if not key.startswith("__") and not inspect.isabstract(value) and not inspect.isbuiltin(value) and not inspect.isfunction(value) and not inspect.isgenerator(value) and not inspect.isgeneratorfunction(value) and not inspect.ismethod(value) and not inspect.ismethoddescriptor(value) and not inspect.isroutine(value) ) return self.default(d) return obj
class C(object): c = "NO" def to_json(self): return {"c": "YES"}
class B(object): b = "B" i = "I" def __init__(self, y): self.y = y def f(self): print("f")
class A(B): a = "A" def __init__(self): self.b = [{"ab": B("y")}] self.c = C()
print(json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True))
|
最佳实践
- 选择合适的方法:根据类的复杂度和具体需求选择合适的序列化方法。对于简单类,继承
dict
或实现 toJSON
方法可能足够;对于复杂类,使用 jsonpickle
或自定义 JSONEncoder
更合适。 - 处理特殊类型:对于
datetime
、Enum
等特殊类型,需要在序列化时进行特殊处理。 - 考虑性能:如果对性能要求较高,可以选择
orjson
等高性能库。
常见问题
1. TypeError: Object of type '...' is not JSON serializable
这通常是因为对象不能直接被 json
模块序列化。可以使用上述方法之一进行处理,如自定义 JSONEncoder
或使用 jsonpickle
。
2. 序列化时丢失某些属性
在自定义序列化方法时,可能会遗漏某些属性。确保在序列化时包含所有需要的属性。
3. 反序列化问题
在反序列化时,可能会遇到类型转换或数据丢失的问题。确保在反序列化时正确处理这些问题,如使用自定义的 object_hook
方法。