如何使一个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

当前回答

如果你正在使用Python3.5+,你可以使用jsons。(PyPi: https://pypi.org/project/jsons/)它将把你的对象(及其所有属性递归地)转换为字典。

import jsons

a_dict = jsons.dump(your_object)

或者如果你想要一个字符串:

a_str = jsons.dumps(your_object)

或者你的类实现了jsons。JsonSerializable:

a_dict = your_object.json

其他回答

解决这个问题有很多方法。'ObjDict' (pip install object)是另一个。重点是提供像javascript一样的对象,它也可以像字典一样最好地处理从JSON加载的数据,但还有其他功能也很有用。这为原始问题提供了另一种解决方案。

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

https://github.com/tobiasholler/PyJSONSerialization/

你知道预期产量是多少吗?例如,这个可以吗?

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

如果还想解码,则必须向JSONDecoder类提供一个自定义object_hook。例如:

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

下面是一个简单功能的简单解决方案:

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

基于Quinten Cabo的回答:

def sterilize(obj):
    """Make an object more ameniable to dumping as json
    """
    if type(obj) in (str, float, int, bool, type(None)):
        return obj
    elif isinstance(obj, dict):
        return {k: sterilize(v) for k, v in obj.items()}
    list_ret = []
    dict_ret = {}
    for a in dir(obj):
        if a == '__iter__' and callable(obj.__iter__):
            list_ret.extend([sterilize(v) for v in obj])
        elif a == '__dict__':
            dict_ret.update({k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']})
        elif a not in ['__doc__', '__module__']:
            aval = getattr(obj, a)
            if type(aval) in (str, float, int, bool, type(None)):
                dict_ret[a] = aval
            elif a != '__class__' and a != '__objclass__' and isinstance(aval, type):
                dict_ret[a] = sterilize(aval)
    if len(list_ret) == 0:
        if len(dict_ret) == 0:
            return repr(obj)
        return dict_ret
    else:
        if len(dict_ret) == 0:
            return list_ret
    return (list_ret, dict_ret)

区别在于

Works for any iterable instead of just list and tuple (it works for NumPy arrays, etc.) Works for dynamic types (ones that contain a __dict__). Includes native types float and None so they don't get converted to string. Classes that have __dict__ and members will mostly work (if the __dict__ and member names collide, you will only get one - likely the member) Classes that are lists and have members will look like a tuple of the list and a dictionary Python3 (that isinstance() call may be the only thing that needs changing)