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

当前回答

任何人都想在没有外部库的情况下使用基本转换,这只是如何使用以下方式覆盖自定义类的__iter__ & __str__函数。

class JSONCustomEncoder(json.JSONEncoder):
    def default(self, obj):
        return obj.__dict__


class Student:
    def __init__(self, name: str, slug: str):
        self.name = name
        self.age = age

    def __iter__(self):
        yield from {
            "name": self.name,
            "age": self.age,
        }.items()

    def __str__(self):
        return json.dumps(
            self.__dict__, cls=JSONCustomEncoder, ensure_ascii=False
        )

通过在dict()中进行包装来使用该对象,从而保留数据。

s = Student("aman", 24)
dict(s)

其他回答

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

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

大多数答案都涉及更改对json.dumps()的调用,这并不总是可能的或可取的(例如,它可能发生在框架组件内部)。

如果你希望能够按原样调用json.dumps(obj),那么一个简单的解决方案是从dict继承:

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

如果你的类只是基本的数据表示,这是可行的,对于更棘手的事情,你总是可以显式地设置键。

要添加另一个选项:您可以使用attrs包和asdict方法。

class ObjectEncoder(JSONEncoder):
    def default(self, o):
        return attr.asdict(o)

json.dumps(objects, cls=ObjectEncoder)

然后再转换回去

def from_json(o):
    if '_obj_name' in o:
        type_ = o['_obj_name']
        del o['_obj_name']
        return globals()[type_](**o)
    else:
        return o

data = JSONDecoder(object_hook=from_json).decode(data)

类看起来像这样

@attr.s
class Foo(object):
    x = attr.ib()
    _obj_name = attr.ib(init=False, default='Foo')

TLDR:复制-粘贴下面的选项1或选项2

真正的/完整的答案:让Pythons json模块与你的类一起工作

AKA,求解:json。dump ({"thing": YOUR_CLASS()})


解释:

Yes, a good reliable solution exists No, there is no python "official" solution By official solution, I mean there is no way (as of 2023) to add a method to your class (like toJSON in JavaScript) and/or no way to register your class with the built-in json module. When something like json.dumps([1,2, your_obj]) is executed, python doesn't check a lookup table or object method. I'm not sure why other answers don't explain this The closest official approach is probably andyhasit's answer which is to inherit from a dictionary. However, inheriting from a dictionary doesn't work very well for many custom classes like AdvancedDateTime, or pytorch tensors. The ideal workaround is this: Mutate json.dumps (affects everywhere, even pip modules that import json) Add def __json__(self) method to your class



选项1:让一个模块来做补丁


PIP安装json-fix (扩展+包装版FancyJohn的回答,谢谢@FancyJohn)

your_class_definition.py

import json_fix

class YOUR_CLASS:
    def __json__(self):
        # YOUR CUSTOM CODE HERE
        #    you probably just want to do:
        #        return self.__dict__
        return "a built-in object that is naturally json-able"

这是它。

使用示例:

from your_class_definition import YOUR_CLASS
import json

json.dumps([1,2, YOUR_CLASS()], indent=0)
# '[\n1,\n2,\n"a built-in object that is naturally json-able"\n]'

生成json。dump适用于Numpy数组,Pandas DataFrames和其他第三方对象,请参阅模块(只有大约2行代码,但需要解释)。




它是如何工作的?嗯…

选项2:补丁json。把你自己


注意:这种方法是简化的,它在已知的edgcase上失败(例如:如果你的自定义类继承了dict或其他内置类),并且它错过了控制外部类的json行为(numpy数组,datetime, dataframes,张量等)。

some_file_thats_imported_before_your_class_definitions.py

# Step: 1
# create the patch
from json import JSONEncoder
def wrapped_default(self, obj):
    return getattr(obj.__class__, "__json__", wrapped_default.default)(obj)
wrapped_default.default = JSONEncoder().default
   
# apply the patch
JSONEncoder.original_default = JSONEncoder.default
JSONEncoder.default = wrapped_default

your_class_definition.py

# Step 2
class YOUR_CLASS:
    def __json__(self, **options):
        # YOUR CUSTOM CODE HERE
        #    you probably just want to do:
        #        return self.__dict__
        return "a built-in object that is natually json-able"

_

其他答案似乎都是“序列化自定义对象的最佳实践/方法”

在这里的文档中已经介绍过了(搜索“complex”可以找到编码复数的例子)

为了给这场11年的大火再添一根柴,我想要一个满足以下条件的解决方案:

只允许使用json.dumps(obj)序列化类FileItem的实例 允许FileItem实例具有属性:FileItem .fname 允许FileItem实例提供给任何库,使用json.dumps(obj)序列化它 不需要将任何其他字段传递给json。转储(如自定义序列化器)

IE:

fileItem = FileItem('filename.ext')
assert json.dumps(fileItem) == '{"fname": "filename.ext"}'
assert fileItem.fname == 'filename.ext'

我的解决方案是:

obj的类是否继承自dict 将每个对象属性映射到底层字典

class FileItem(dict):
    def __init__(self, fname):
        self['fname'] = fname

    #fname property
    fname: str = property()
    @fname.getter
    def fname(self):
        return self['fname']

    @fname.setter
    def fname(self, value: str):
        self['fname'] = value

    #Repeat for other properties

是的,如果你有很多属性,这有点冗长,但它是JSONSerializable,它的行为像一个对象,你可以把它给任何库,去json.dumps(obj)它。