How to make a class JSON serializable
如何使python类可序列化?
一个简单的类:
1 2 3 | class FileItem: def __init__(self, fname): self.fname = fname |
我应该怎么做才能获得以下输出:
1 | json.dumps() |
无误(
Here is a simple solution for a simple feature:
Instead of a JSON serializable class, implement a serializer method:
1 2 3 4 5 6 | 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()) |
意志产出:
1 2 3 4 5 6 7 | { "age": 35, "dog": { "name":"Apollo" }, "name":"Onur" } |
你对预期产出有什么想法吗?例如,这能做到吗?
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 | >>> f = FileItem("/foo/bar") >>> magic(f) '{"fname":"/foo/bar <div class="suo-content">[collapse title=""]<ul><li>使用<wyn>__dict__</wyn>在所有情况下都不起作用。如果在对象实例化后未设置属性,则可能无法完全填充<wyn>__dict__</wyn>。在上面的例子中,您是可以的,但是如果您有您也想要编码的类属性,那么这些属性将不会在<wyn>__dict__</wyn>中列出,除非它们在类'<wyn>__init__</wyn>调用中被修改过,或者在对象被实例化后以其他方式被修改过。</li><li>+1,但是用作对象钩子的<wyn>from_json()</wyn>函数应该有一个<wyn>else: return json_object</wyn>语句,这样它也可以处理一般对象。</li><li>@如果你在一个新的风格类上使用<wyn>__slots__</wyn>,那么krishardy <wyn>__dict__</wyn>也不起作用。</li><li>这比使用cpickle快吗?</li><li>您可以如上所述使用自定义<wyn>JSONEncoder</wyn>创建自定义协议,例如检查<wyn>__json_serializable__</wyn>方法的存在并调用它以获取对象的JSON可序列化表示。这将与其他的Python模式保持一致,如<wyn>__getitem__</wyn>、<wyn>__str__</wyn>、<wyn>__eq__</wyn>和<wyn>__len__</wyn>。</li><li>对python来说是新的吗?我们可以使用这些库(<wyn>json</wyn>或<wyn>simplejson</wyn>来序列化/反序列化python开发人员(如pandas数据帧、系列等)广泛使用的对象吗?</li><li><wyn>__dict__</wyn>也不会递归工作,例如,如果对象的属性是另一个对象。</li><li>如果给定的数据是非标准的,那么<wyn>JSONEncoder</wyn>只调用子类的<wyn>default</wyn>方法。</li><li>@Mahesha999对数据帧、序列等使用df.to_json()方法</li></ul>[/collapse]</div><hr> <p> For more complex classes you could consider the tool jsonpickle: </p> <blockquote> <p> jsonpickle is a Python library for serialization and deserialization of complex Python objects to and from JSON. </p> <p> The standard Python libraries for encoding Python into JSON, such as the stdlib’s json, simplejson, and demjson, can only handle Python primitives that have a direct JSON equivalent (e.g. dicts, lists, strings, ints, etc.). jsonpickle builds on top of these libraries and allows more complex data structures to be serialized to JSON. jsonpickle is highly configurable and extendable–allowing the user to choose the JSON backend and add additional backends. </p> </blockquote> <p> (link to jsonpickle on PyPi) </p> <div class="suo-content">[collapse title=""]<ul><li>来自C,这是我所期待的。一条简单的单行线,不会弄乱课堂。</li><li>jsonpickle太棒了。它非常适合于具有许多级别的类的大型、复杂、杂乱的对象。</li><li>是否有将此保存到文件的正确方法示例?文档只显示如何对<wyn>jsonpickle</wyn>对象进行编码和解码。此外,这无法解码包含熊猫数据帧的听写。</li><li>@用户5359531可以使用<wyn>obj = jsonpickle.decode(file.read())</wyn>和<wyn>file.write(jsonpickle.encode(obj))</wyn>。</li><li>针对Django的一个问题是:使用jsonpickle序列化会话数据是否具有与pickle相同的漏洞?(如本文所述docs.djangoproject.com/en/1.11/topics/http/sessions/&hellip;)?</li><li>@鲍尔伯曼是的。</li></ul>[/collapse]</div><hr><P>大多数答案都涉及到更改对json.dumps()的调用,这并不总是可能的或可取的(例如,它可能发生在框架组件中)。</P><P>如果您希望能够按原样调用json.dumps(obj),那么一个简单的解决方案就是从dict继承:</P>[cc lang="python"]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 |
如果您的类只是基本的数据表示,那么对于更棘手的事情,您总是可以显式地设置键。
Another option is to wrap JSON dumping in its own class:
1 2 3 4 5 6 7 8 | import json class FileItem: def __init__(self, fname): self.fname = fname def __repr__(self): return json.dumps(self.__dict__) |
或者,更好的是,从
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> f = FileItem('/foo/bar') >>> f.toJson() '{"fname":"/foo/bar <div class="suo-content">[collapse title=""]<ul><li>嗨,我真的不喜欢这种"自定义编码器"方法,如果您能使类JSON可串行化就更好了。我试着,试着,什么也不试。你知道怎么做吗?问题是,JSON模块针对内置的Python类型测试类,甚至还说定制类可以生成编码器:)。它能被伪造吗?所以我可以对我的类做一些事情,让它的行为像简单的列表到JSON模块?我尝试子类检查和实例检查,但什么都没有。</li><li>@如果所有类属性值都是可序列化的,并且您不介意黑客攻击,那么您可以从主要类型(可能是dict)继承肾上腺素。您也可以使用jsonpickle或json_技巧,而不是标准的(仍然是自定义编码器,但不需要编写或调用)。前者对实例进行pickle处理,后者将其存储为属性dict,您可以通过实现<wyn>__json__encode__</wyn>/<wyn>__json_decode__</wyn>(公开:我做了最后一个)。</li></ul>[/collapse]</div><hr> <p> I like Onur's answer but would expand to include an optional <wyn>toJSON()</wyn> method for objects to serialize themselves: </p> [cc lang="python"]def dumper(obj): try: return obj.toJSON() except: return obj.__dict__ print json.dumps(some_big_object, default=dumper, indent=2) |
I came across this problem the other day and implemented a more general version of an Encoder for Python objects that can handle nested objects and inherited fields:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 |
例子:
1 2 3 4 5 6 | class C(object): c ="NO" def to_json(self): return {"c":"YES <div class="suo-content">[collapse title=""]<ul><li>虽然这有点旧…我面临一些循环导入错误。所以,我不是在最后一行中使用<wyn>return obj</wyn>,而是使用<wyn>return super(ObjectEncoder, self).default(obj)</wyn>。这里参考</li></ul>[/collapse]</div><hr><P>只需向类中添加<wyn>to_json</wyn>方法,如下所示:</P>[cc lang="python"]def to_json(self): return self.message # or how you want it to be serialized |
把这段代码(从这个答案)添加到最上面的某个地方:
1 2 3 4 5 6 7 | from json import JSONEncoder def _default(self, obj): return getattr(obj.__class__,"to_json", _default.default)(obj) _default.default = JSONEncoder().default JSONEncoder.default = _default |
当导入JSON模块时,它将monkey补丁,所以jsonEncoder.default()自动检查特殊的"to_json()"方法,并使用它对找到的对象进行编码。
正如奥纳所说,但这次您不必更新项目中的每个
1 2 3 4 5 6 7 8 9 10 11 | import simplejson class User(object): def __init__(self, name, mail): self.name = name self.mail = mail def _asdict(self): return self.__dict__ print(simplejson.dumps(User('alice', '[email protected]'))) |
如果使用标准
1 2 3 4 5 | import json def default(o): return o._asdict() print(json.dumps(User('alice', '[email protected]'), default=default)) |
1 2 3 4 | import json import jsonpickle ... print json.dumps(json.loads(jsonpickle.encode(object)), indent=2) |
请注意,它们仍然无法打印对象方法。
这个类可以做到这一点,它将对象转换为标准JSON。
1 2 3 4 5 6 7 | import json class Serializer(object): @staticmethod def serialize(object): return json.dumps(object, default=lambda o: o.__dict__.values()[0]) |
用途:
1 | Serializer.serialize(my_object) |
在
如果你用的是python3.5+,你可以用
1 2 3 | import jsons a_dict = jsons.dump(your_object) |
或者如果你想要一个字符串:
1 | a_str = jsons.dumps(your_object) |
或者如果您的类实现了
1 | a_dict = your_object.json |
雅拉科给出了一个非常清楚的回答。我需要解决一些小问题,但这是可行的:
代码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 | # Your custom class class MyCustom(object): def __json__(self): return { 'a': self.a, 'b': self.b, '__python__': 'mymodule.submodule:MyCustom.from_json', } to_json = __json__ # supported by simplejson @classmethod def from_json(cls, json): obj = cls() obj.a = json['a'] obj.b = json['b'] return obj # Dumping and loading import simplejson obj = MyCustom() obj.a = 3 obj.b = 4 json = simplejson.dumps(obj, for_json=True) # Two-step loading obj2_dict = simplejson.loads(json) obj2 = MyCustom.from_json(obj2_dict) # Make sure we have the correct thing assert isinstance(obj2, MyCustom) assert obj2.__dict__ == obj.__dict__ |
请注意,加载需要两个步骤。目前,
使用aljohri的方法,我检查方法的流行性:
序列化(python->json):
to_json 266595 2018-06-27toJSON 号:2018-06-27 96307__json__ 2018-06-27:8504for_json 号:6937,2018-06-27
反序列化(json->python):
from_json 号:2018-06-27 226101
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 | import json class Foo(object): def __init__(self): self.bar = 'baz' self._qux = 'flub' def somemethod(self): pass def default(instance): return {k: v for k, v in vars(instance).items() if not str(k).startswith('_')} json_foo = json.dumps(Foo(), default=default) assert '{"bar":"baz <div class="suo-content">[collapse title=""]<ul><li>From Doc:参数<wyn>default(obj)</wyn>是一个函数,应该返回obj的可序列化版本或引发typeerror。默认的<wyn>default</wyn>只会引发类型错误。</li></ul>[/collapse]</div><p><center>[wp_ad_camp_3]</center></p><hr><P>jsonweb似乎是我最好的解决方案。请参阅http://www.jsonweb.info/en/latest/</P>[cc lang="python"]from jsonweb.encode import to_object, dumper @to_object() class DataModel(object): def __init__(self, id, value): self.id = id self.value = value >>> data = DataModel(5,"foo") >>> dumper(data) '{"__type__":"DataModel","id": 5,"value":"foo <div class="suo-content">[collapse title=""]<ul><li>它对嵌套对象是否有效?包括解码和编码</li></ul>[/collapse]</div><hr><P>这对我很有效:</P>[cc lang="python"]class JsonSerializable(object): def serialize(self): return json.dumps(self.__dict__) def __repr__(self): return self.serialize() @staticmethod def dumper(obj): if"serialize" in dir(obj): return obj.serialize() return obj.__dict__ |
然后
1 2 | class FileItem(JsonSerializable): ... |
和
1 | log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2)) |
如果您不介意为它安装一个包,您可以使用JSON技巧:
1 | pip install json-tricks |
之后,您只需要从
1 2 | from json_tricks import dumps json_str = dumps(cls_instance, indent=4) |
将给予
1 2 3 4 5 6 7 8 9 10 11 12 | { "__instance_type__": [ "module_name.test_class", "MyTestCls" ], "attributes": { "attr":"val", "dct_attr": { "hello": 42 } } } |
基本上就是这样!
一般来说,这会很有效。也有一些例外,例如,如果在
显然,加载也有效(否则重点是什么):
1 2 | from json_tricks import loads json_str = loads(json_str) |
这确实假定可以导入
如果您想自定义某个对象如何(反)序列化,可以向类中添加特殊方法,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class CustomEncodeCls: def __init__(self): self.relevant = 42 self.irrelevant = 37 def __json_encode__(self): # should return primitive, serializable types like dict, list, int, string, float... return {'relevant': self.relevant} def __json_decode__(self, **attrs): # should initialize all properties; note that __init__ is not called implicitly self.relevant = attrs['relevant'] self.irrelevant = 12 |
例如,它只序列化属性参数的一部分。
作为一个免费的奖励,您可以(反)序列化numpy数组、日期和时间、有序的映射,以及在JSON中包含注释的能力。
免责声明:我创建了json_技巧,因为我和你有相同的问题。
这是我的3美分…
这演示了类似于python对象的树的显式JSON序列化。
注意:如果你真的想要这样的代码,你可以使用TwistedFielPATH类。
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 | import json, sys, os class File: def __init__(self, path): self.path = path def isdir(self): return os.path.isdir(self.path) def isfile(self): return os.path.isfile(self.path) def children(self): return [File(os.path.join(self.path, f)) for f in os.listdir(self.path)] def getsize(self): return os.path.getsize(self.path) def getModificationTime(self): return os.path.getmtime(self.path) def _default(o): d = {} d['path'] = o.path d['isFile'] = o.isfile() d['isDir'] = o.isdir() d['mtime'] = int(o.getModificationTime()) d['size'] = o.getsize() if o.isfile() else 0 if o.isdir(): d['children'] = o.children() return d folder = os.path.abspath('.') json.dump(File(folder), sys.stdout, default=_default) |
当我试图将Peewee的模型存储到PostgreSQL
经过一段时间的挣扎,这里是一般的解决办法。
我的解决方案的关键是浏览python的源代码,认识到代码文档(在这里描述)已经解释了如何扩展现有的
假设您当前的模型包含一些不能序列化到JSON的字段,并且包含JSON字段的模型最初如下所示:
1 2 | class SomeClass(Model): json_field = JSONField() |
像这样定义一个自定义
1 2 3 4 5 6 7 8 9 | class CustomJsonEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, SomeTypeUnsupportedByJsonDumps): return < whatever value you want > return json.JSONEncoder.default(self, obj) @staticmethod def json_dumper(obj): return json.dumps(obj, cls=CustomJsonEncoder) |
然后在你的
1 2 | class SomeClass(Model): json_field = JSONField(dumps=CustomJsonEncoder.json_dumper) |
关键是上面的
例如,以下是我如何支持从
1 2 3 4 5 6 7 8 | class TransactionType(Enum): CURRENT = 1 STACKED = 2 def default(self, obj): if isinstance(obj, TransactionType): return obj.value return json.JSONEncoder.default(self, obj) |
最后,使用上面实现的代码,您可以将任何peewee模型转换为下面这样的JSON可序列化对象:
1 2 3 | peewee_model = WhateverPeeweeModel() new_model = SomeClass() new_model.json_field = model_to_dict(peewee_model) |
虽然上面的代码(有点)特定于Peewee,但是我认为:
如有任何问题,请发表在评论部分。谢谢!
我选择使用修饰符来解决日期时间对象序列化问题。这是我的代码:
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 | #myjson.py #Author: jmooremcc 7/16/2017 import json from datetime import datetime, date, time, timedelta """ This module uses decorators to serialize date objects using json The filename is myjson.py In another module you simply add the following import statement: from myjson import json json.dumps and json.dump will then correctly serialize datetime and date objects """ def json_serial(obj): """JSON serializer for objects not serializable by default json code""" if isinstance(obj, (datetime, date)): serial = str(obj) return serial raise TypeError ("Type %s not serializable" % type(obj)) def FixDumps(fn): def hook(obj): return fn(obj, default=json_serial) return hook def FixDump(fn): def hook(obj, fp): return fn(obj,fp, default=json_serial) return hook json.dumps=FixDumps(json.dumps) json.dump=FixDump(json.dump) if __name__=="__main__": today=datetime.now() data={'atime':today, 'greet':'Hello'} str=json.dumps(data) print str |
通过导入上述模块,我的其他模块以正常方式(不指定默认关键字)使用JSON来序列化包含日期时间对象的数据。对于json.dumps和json.dump,将自动调用日期时间序列化程序代码。
我最喜欢Lost Koder的方法。我在试图序列化成员/方法不可序列化的更复杂的对象时遇到问题。下面是我在更多对象上工作的实现:
1 2 3 4 5 6 7 8 9 10 11 12 | class Serializer(object): @staticmethod def serialize(obj): def check(o): for k, v in o.__dict__.items(): try: _ = json.dumps(v) o.__dict__[k] = v except TypeError: o.__dict__[k] = str(v) return o return json.dumps(check(obj).__dict__, indent=2) |
我想出了自己的解决办法。使用此方法,传递要序列化的任何文档(dict、list、objectid等)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | def getSerializable(doc): # check if it's a list if isinstance(doc, list): for i, val in enumerate(doc): doc[i] = getSerializable(doc[i]) return doc # check if it's a dict if isinstance(doc, dict): for key in doc.keys(): doc[key] = getSerializable(doc[key]) return doc # Process ObjectId if isinstance(doc, ObjectId): doc = str(doc) return doc # Use any other custom serializting stuff here... # For the rest of stuff return doc |
这是一个小库,它将对象及其所有子对象序列化为JSON,并将其解析回:
https://github.com/toubs/pyjsonserialization网站/
如果你能安装一个软件包,我建议你尝试一下Dill,它对我的项目很好。这个包的一个好处是它具有与
(完全反披露:我与DILL项目没有任何关联,也从未参与过该项目。)
安装程序包:
1 | pip install dill |
然后编辑代码以导入
1 2 | # import pickle import dill as pickle |
运行您的脚本,看看它是否有效。(如果是这样,您可能希望清理代码,以便不再隐藏
dill can pickle the following standard types:none, type, bool, int, long, float, complex, str, unicode, tuple,
list, dict, file, buffer, builtin, both old and new style classes,
instances of old and new style classes, set, frozenset, array,
functions, exceptions
dill can also pickle more ‘exotic’ standard types:functions with yields, nested functions, lambdas, cell, method,
unboundmethod, module, code, methodwrapper, dictproxy,
methoddescriptor, getsetdescriptor, memberdescriptor,
wrapperdescriptor, xrange, slice, notimplemented, ellipsis, quit
dill cannot yet pickle these standard types:frame, generator, traceback
这个问题有很多解决办法。"objdict(pip安装objdict)是另一个。重点是提供类似于javascript的对象,这些对象也可以像字典一样处理从JSON加载的数据,但是还有其他一些功能也很有用。这为原始问题提供了另一种解决方案。