Serializing class instance to JSON
我正在尝试创建类实例的JSON字符串表示并且有困难。 假设这个类是这样构建的:
1 2 3 | class testclass: value1 ="a" value2 ="b" |
对json.dumps的调用是这样的:
1 2 | t = testclass() json.dumps(t) |
它失败并且告诉我测试类不是JSON可序列化的。
1 | TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable |
我也尝试过使用pickle模块:
1 2 | t = testclass() print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL)) |
它提供类实例信息,但不提供类实例的序列化内容。
1 2 3 | b'\x80\x03c__main__ testclass q\x00)\x81q\x01}q\x02b.' |
我究竟做错了什么?
基本问题是JSON编码器
一个好的解决方案是让你的类继承自
一个简单的解决方案是在该实例的
1 2 3 4 5 6 7 8 9 | class Foo(object): def __init__(self): self.x = 1 self.y = 2 foo = Foo() s = json.dumps(foo) # raises TypeError with"is not JSON serializable" s = json.dumps(foo.__dict__) # s set to: {"x":1,"y":2} |
本博文中讨论了上述方法:
&nbsp;&nbsp;&nbsp;&nbsp;使用__dict__将任意Python对象序列化为JSON
有一种方法对我很有用,你可以尝试:
1 2 3 4 5 6 7 8 9 10 11 12 | def serialize(obj): """JSON serializer for objects not serializable by default json code""" if isinstance(obj, date): serial = obj.isoformat() return serial if isinstance(obj, time): serial = obj.isoformat() return serial return obj.__dict__ |
前两个ifs用于日期和时间序列化
然后为任何其他对象返回
最后的电话看起来像:
1 | json.dumps(myObj, default=serialize) |
当你序列化一个集合并且你不想为每个对象显式调用
到目前为止,对我来说工作非常好,期待你的想法。
您可以在
1 | json.dumps(obj, default=lambda x: x.__dict__) |
说明:
形成文档(2.7,3.6):
1 2 | ``default(obj)`` is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. |
(适用于Python 2.7和Python 3.x)
注意:在这种情况下,您需要
我从@ phihag的回答中首先了解到这一点。发现它是最简单,最干净的工作方式。
我只是做:
1 | data=json.dumps(myobject.__dict__) |
这不是完整的答案,如果你有某种复杂的对象类,你肯定不会得到所有东西。但是我将它用于我的一些简单对象。
它可以很好地工作的是从OptionParser模块获得的"选项"类。
这里是JSON请求本身。
1 2 3 4 5 6 | def executeJson(self, url, options): data=json.dumps(options.__dict__) if options.verbose: print data headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} return requests.post(url, data, headers=headers) |
使用jsonpickle
1 2 3 4 | import jsonpickle object = YourClass() json_object = jsonpickle.encode(object) |
以下是两个用于序列化任何非复杂类的简单函数,如前所述,没什么特别的。
我将它用于配置类型的东西,因为我可以在没有代码调整的情况下向类中添加新成员。
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 | import json class SimpleClass: def __init__(self, a=None, b=None, c=None): self.a = a self.b = b self.c = c def serialize_json(instance=None, path=None): dt = {} dt.update(vars(instance)) with open(path,"w") as file: json.dump(dt, file) def deserialize_json(cls=None, path=None): def read_json(_path): with open(_path,"r") as file: return json.load(file) data = read_json(path) instance = object.__new__(cls) for key, value in data.items(): setattr(instance, key, value) return instance # Usage: Create class and serialize under Windows file system. write_settings = SimpleClass(a=1, b=2, c=3) serialize_json(write_settings, r"c:\temp\test.json") # Read back and rehydrate. read_settings = deserialize_json(SimpleClass, r"c:\temp\test.json") # results are the same. print(vars(write_settings)) print(vars(read_settings)) # output: # {'c': 3, 'b': 2, 'a': 1} # {'c': 3, 'b': 2, 'a': 1} |
JSON并不是用于序列化任意Python对象。它非常适合序列化
https://github.com/jsonpickle/jsonpickle
我相信在接受的答案中建议不是继承,而是使用多态。否则,您必须有一个大的if else语句来自定义每个对象的编码。这意味着为JSON创建一个通用的默认编码器:
1 2 3 4 5 | def jsonDefEncoder(obj): if hasattr(obj, 'jsonEnc'): return obj.jsonEnc() else: #some default behavior return obj.__dict__ |
然后在要序列化的每个类中都有一个
1 2 3 4 5 | class A(object): def __init__(self,lengthInFeet): self.lengthInFeet=lengthInFeet def jsonEnc(self): return {'lengthInMeters': lengthInFeet * 0.3 } # each foot is 0.3 meter |
然后你打电话给
Python3.x
我可以用我的知识达到最好的方法就是这个
请注意,此代码也会处理set()
这种方法是通用的,只需要扩展类(在第二个例子中)
请注意,我只是对文件进行操作,但很容易根据自己的喜好修改行为。
然而,这是一个CoDec。
通过更多的工作,您可以通过其他方式构建您的课程。
我假设一个默认构造函数来实例化它,然后我更新类dict。
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | import json import collections class JsonClassSerializable(json.JSONEncoder): REGISTERED_CLASS = {} def register(ctype): JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype def default(self, obj): if isinstance(obj, collections.Set): return dict(_set_object=list(obj)) if isinstance(obj, JsonClassSerializable): jclass = {} jclass["name"] = type(obj).__name__ jclass["dict"] = obj.__dict__ return dict(_class_object=jclass) else: return json.JSONEncoder.default(self, obj) def json_to_class(self, dct): if '_set_object' in dct: return set(dct['_set_object']) elif '_class_object' in dct: cclass = dct['_class_object'] cclass_name = cclass["name"] if cclass_name not in self.REGISTERED_CLASS: raise RuntimeError( "Class {} not registered in JSON Parser" .format(cclass["name"]) ) instance = self.REGISTERED_CLASS[cclass_name]() instance.__dict__ = cclass["dict"] return instance return dct def encode_(self, file): with open(file, 'w') as outfile: json.dump( self.__dict__, outfile, cls=JsonClassSerializable, indent=4, sort_keys=True ) def decode_(self, file): try: with open(file, 'r') as infile: self.__dict__ = json.load( infile, object_hook=self.json_to_class ) except FileNotFoundError: print("Persistence load failed" "'{}' do not exists".format(file) ) class C(JsonClassSerializable): def __init__(self): self.mill ="s" JsonClassSerializable.register(C) class B(JsonClassSerializable): def __init__(self): self.a = 1230 self.c = C() JsonClassSerializable.register(B) class A(JsonClassSerializable): def __init__(self): self.a = 1 self.b = {1, 2} self.c = B() JsonClassSerializable.register(A) A().encode_("test") b = A() b.decode_("test") print(b.a) print(b.b) print(b.c.a) |
编辑
通过更多的研究,我发现了一种无需SUPERCLASS寄存器方法调用的概括,使用元类
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | import json import collections REGISTERED_CLASS = {} class MetaSerializable(type): def __call__(cls, *args, **kwargs): if cls.__name__ not in REGISTERED_CLASS: REGISTERED_CLASS[cls.__name__] = cls return super(MetaSerializable, cls).__call__(*args, **kwargs) class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable): def default(self, obj): if isinstance(obj, collections.Set): return dict(_set_object=list(obj)) if isinstance(obj, JsonClassSerializable): jclass = {} jclass["name"] = type(obj).__name__ jclass["dict"] = obj.__dict__ return dict(_class_object=jclass) else: return json.JSONEncoder.default(self, obj) def json_to_class(self, dct): if '_set_object' in dct: return set(dct['_set_object']) elif '_class_object' in dct: cclass = dct['_class_object'] cclass_name = cclass["name"] if cclass_name not in REGISTERED_CLASS: raise RuntimeError( "Class {} not registered in JSON Parser" .format(cclass["name"]) ) instance = REGISTERED_CLASS[cclass_name]() instance.__dict__ = cclass["dict"] return instance return dct def encode_(self, file): with open(file, 'w') as outfile: json.dump( self.__dict__, outfile, cls=JsonClassSerializable, indent=4, sort_keys=True ) def decode_(self, file): try: with open(file, 'r') as infile: self.__dict__ = json.load( infile, object_hook=self.json_to_class ) except FileNotFoundError: print("Persistence load failed" "'{}' do not exists".format(file) ) class C(JsonClassSerializable): def __init__(self): self.mill ="s" class B(JsonClassSerializable): def __init__(self): self.a = 1230 self.c = C() class A(JsonClassSerializable): def __init__(self): self.a = 1 self.b = {1, 2} self.c = B() A().encode_("test") b = A() b.decode_("test") print(b.a) # 1 print(b.b) # {1, 2} print(b.c.a) # 1230 print(b.c.c.mill) # s |
如何开始这样做有一些很好的答案。但是有一些事情要记住:
- 如果实例嵌套在大型数据结构中会怎么样?
- 如果还想要班级名称怎么办?
- 如果要反序列化实例,该怎么办?
-
如果您使用
__slots__ 而不是__dict__ 怎么办? - 如果你不想自己做怎么办?
json-tricks是一个图书馆(我做了,其他人做出了贡献)已经能够做到这一点很长一段时间了。例如:
1 2 3 4 5 6 7 8 9 | class MyTestCls: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) cls_instance = MyTestCls(s='ub', dct={'7': 7}) json = dumps(cls_instance, indent=4) instance = loads(json) |
你会得到你的实例。这里的json看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 | { "__instance_type__": [ "json_tricks.test_class", "MyTestCls" ], "attributes": { "s":"ub", "dct": { "7": 7 } } } |
如果您想制作自己的解决方案,可以查看
它还有其他类型,如numpy数组,日期时间,复数;它还允许评论。