如何使一个类JSON可串行化

如何使一个Python类可序列化?

一个简单的类:

class FileItem: def __init__(self, fname): self.fname = fname 

我应该做什么才能够得到的输出:

 json.dumps() 

没有错误( FileItem instance at ... is not JSON serializable

你有关于预期产出的想法吗? 例如这样做吗?

 >>> 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 kwarg传递给json.dumps()方法:

 json.dumps(cls=MyEncoder) 

如果你也想解码,那么你必须提供一个自定义的object_hookJSONDecoder类。 例如

 >>> 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> >>> 

这是一个简单的function简单的解决scheme:

.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" } 

对于更复杂的类,你可以考虑工具jsonpickle

jsonpickle是一个Python库,用于将复杂的Python对象序列化并从JSON中反序列化。

用于将Python编码为JSON的标准Python库(例如stdlib的json,simplejson和demjson)只能处理具有直接JSON等效的Python基元(例如,string,列表,string,整数等)。 jsonpicklebuild立在这些库之上,允许将更复杂的数据结构序列化为JSON。 jsonpickle是高度可configuration和可扩展的 – 允许用户selectJSON后端并添加额外的后端。

(在PyPi上的jsonpickle)

大部分答案都涉及将调用更改为json.dumps() ,这并不总是可行或可取(可能发生在框架组件内部)。

如果你想能够直接调用json.dumps(obj) ,那么一个简单的解决scheme是从dictinheritance的:

 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 

如果你的类只是基本的数据表示,这可以工作,对于棘手的事情,你总是可以明确地设置键。

我喜欢Onur的答案,但会扩大到包括一个可选的toJSON()方法的对象序列化自己:

 def dumper(obj): try: return obj.toJSON() except: return obj.__dict__ print json.dumps(some_big_object, default=dumper, indent=2) 

另一种情况是将JSON转储到自己的类中:

 import json class FileItem: def __init__(self, fname): self.fname = fname def __repr__(self): return json.dumps(self.__dict__) 

或者甚至从JSONSerializable类inheritanceFileItem类:

 import json class JSONSerializable(object): def __repr__(self): return json.dumps(self.__dict__) class FileItem(JSONSerializable): def __init__(self, fname): self.fname = fname 

testing:

 >>> f = FileItem('/foo/bar') >>> f '{"fname": "/foo/bar"}' >>> str(f) # string coercion '{"fname": "/foo/bar"}' 

有一天,我遇到了这个问题,并为Python对象实现了一个更通用的编码器,它可以处理嵌套的对象inheritance的字段

 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) 

结果:

 { "a": "A", "b": [ { "ab": { "b": "B", "i": "I", "y": "y" } } ], "c": { "c": "YES" }, "i": "I" } 
 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', 'alice@mail.com'))) 

如果使用标准的json ,你需要定义一个default函数

 import json def default(o): return o._asdict() print(json.dumps(User('alice', 'alice@mail.com'), default=default)) 

json在它可以打印的对象方面是有限的,而jsonpickle (你可能需要一个pip install jsonpickle )是有限的,它不能缩进文本。 如果你想检查一个你不能改变的对象的内容,我仍然找不到比以下更直接的方法:

  import json import jsonpickle ... print json.dumps(json.loads(jsonpickle.encode(object)), indent=2) 

请注意,它们仍然无法打印对象方法。

只需要添加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 

这将导入猴子补丁json模块时,JSONEncoder.default()自动检查一个特殊的“to_json()”方法,并使用它来编码对象,如果find。

就像Onur说的,但是这次你不必更新项目中的每个json.dumps()

这个类可以做到这一点,它将对象转换为标准的JSON。

 import json class Serializer(object): @staticmethod def serialize(object): return json.dumps(object, default=lambda o: o.__dict__.values()[0]) 

用法:

 Serializer.serialize(my_object) 

python2.7python3

 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"}' == json_foo print(json_foo) 

jsonweb似乎是我最好的解决scheme。 见http://www.jsonweb.info/en/latest/

 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"}' 

这是我的3分钱…
这演示了一个树型python对象的显式json序列化。
注意:如果你真的想要这样的代码,你可以使用扭曲的FilePath类。

 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) 

这是一个小型的库,它将所有的子对象序列化为JSON,并将其parsing回来:

https://github.com/Toubs/PyJSONSerialization/

如果你不介意为它安装一个包,你可以使用json-tricks :

 pip install json-tricks 

之后,你只需要从json_tricks而不是json导入dump(s) ,它通常会工作:

 from json_tricks import dumps json_str = dumps(cls_instance, indent=4) 

这将给

 { "__instance_type__": [ "module_name.test_class", "MyTestCls" ], "attributes": { "attr": "val", "dct_attr": { "hello": 42 } } } 

基本上就是这样!


这将在一般情况下工作。 有一些例外,例如,如果特殊的事情发生在__new__ ,或更多的元类魔术正在进行。

显然加载也可以(否则有什么意义):

 from json_tricks import loads json_str = loads(json_str) 

这确实假定可以导入module_name.test_class.MyTestCls ,并且不以非兼容方式更改。 你会得到一个实例 ,而不是一些字典或东西,它应该是一个相同的副本,你倾倒。

如果你想自定义如何获取(de)序列化,你可以添加特殊的方法到你的类,如下所示:

 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 

例如,它仅序列化部分属性参数。

作为一个免费的奖励,你可以得到(de)序列化的numpy数组,date和时间,有序地图,以及在json中包含注释的能力。

免责声明:我创build了json_tricks ,因为我和你有同样的问题。

我想出了我自己的解决scheme。 使用此方法,传递任何文档( 字典列表ObjectId等)来序列化。

 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 

这个问题有很多方法。 'ObjDict'(pip install objdict)是另一个。 强调提供类似JavaScript的对象,它也可以像字典一样处理从JSON加载的数据,但也有其他的function可以使用。 这为原来的问题提供了另一种解决scheme。

我select使用装饰器来解决date时间对象序列化问题。 这是我的代码:

 #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(不指定default关键字)来序列化包含date时间对象的数据。 json.dumps和json.dump会自动调用date时间序列化程序代码。

我最喜欢迷失Koder的方法。 当我试图序列化更复杂的对象,而成员/方法不可序列化时,我遇到了一些问题。 这是我的实现对更多的对象工作:

 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)