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

当前回答

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

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

其他回答

你们为什么要把事情搞得这么复杂?这里有一个简单的例子:

#!/usr/bin/env python3

import json
from dataclasses import dataclass

@dataclass
class Person:
    first: str
    last: str
    age: int

    @property
    def __json__(self):
        return {
            "name": f"{self.first} {self.last}",
            "age": self.age
        }

john = Person("John", "Doe", 42)
print(json.dumps(john, indent=4, default=lambda x: x.__json__))

这样你也可以序列化嵌套类,因为__json__返回一个python对象而不是字符串。不需要使用JSONEncoder,因为使用简单lambda的默认参数也可以很好地工作。

我使用@property代替了一个简单的函数,因为这样感觉更自然和现代。@dataclass也只是一个例子,它也适用于“普通”类。

一个非常简单的一行程序解决方案

import json

json.dumps(your_object, default=lambda __o: __o.__dict__)

结束!

下面是一个测试。

import json
from dataclasses import dataclass


@dataclass
class Company:
    id: int
    name: str

@dataclass
class User:
    id: int
    name: str
    email: str
    company: Company


company = Company(id=1, name="Example Ltd")
user = User(id=1, name="John Doe", email="john@doe.net", company=company)


json.dumps(user, default=lambda __o: __o.__dict__)

输出:

{
  "id": 1, 
  "name": "John Doe", 
  "email": "john@doe.net", 
  "company": {
    "id": 1, 
    "name": "Example Ltd"
  }
}

这对我来说很有效:

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__

然后

class FileItem(JsonSerializable):
    ...

and

log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))

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

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

正如在许多其他答案中提到的,您可以将函数传递给json。转储将不是默认支持的类型之一的对象转换为受支持的类型。令人惊讶的是,他们都没有提到最简单的情况,即使用内置函数vars将对象转换为包含其所有属性的dict:

json.dumps(obj, default=vars)

注意,这只涵盖了基本的情况,如果你需要对某些类型进行更具体的序列化(例如排除某些属性或没有__dict__属性的对象),你需要使用自定义函数或JSONEncoder,如其他答案中所述。