我试图创建一个类实例的JSON字符串表示,有困难。假设这个类是这样构建的:

class testclass:
    value1 = "a"
    value2 = "b"

对json的调用。转储是这样的:

t = testclass()
json.dumps(t)

它失败了,告诉我测试类不是JSON序列化的。

TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable

我也尝试过使用pickle模块:

t = testclass()
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))

它提供类实例的信息,而不是类实例的序列化内容。

b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'

我做错了什么?


当前回答

这里有一些关于如何开始做这件事的很好的答案。但有一些事情要记住:

如果实例嵌套在大型数据结构中怎么办? 如果还想要类名呢? 如果要反序列化实例该怎么办? 如果你使用__slots__而不是__dict__呢? 如果你只是不想自己动手呢?

json-tricks是一个库(我创建的,其他人贡献的),它已经能够做到这一点很长时间了。例如:

class MyTestCls:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

cls_instance = MyTestCls(s='ub', dct={'7': 7})

json = dumps(cls_instance, indent=4)
instance = loads(json)

您将得到您的实例。这里的json是这样的:

{
    "__instance_type__": [
        "json_tricks.test_class",
        "MyTestCls"
    ],
    "attributes": {
        "s": "ub",
        "dct": {
            "7": 7
        }
    }
}

如果你喜欢自己做解决方案,你可以查看json-tricks的源代码,以免忘记一些特殊情况(如__slots__)。

它也可以处理其他类型,比如numpy数组,datetimes,复数;它还允许注释。

其他回答

我为此做了一个函数,效果很好:

def serialize(x,*args,**kwargs):
    kwargs.setdefault('default',lambda x:getattr(x,'__dict__',dict((k,getattr(x,k) if not callable(getattr(x,k)) else repr(getattr(x,k))) for k in dir(x) if not (k.startswith('__') or isinstance(getattr(x,k),x.__class__)))))
    return json.dumps(x,*args,**kwargs)

Python3.x

以我的知识,我能找到的最好的方法是。 注意,这段代码也处理set()。 这种方法是通用的,只需要类的扩展(在第二个例子中)。 请注意,我只是对文件执行此操作,但是很容易根据自己的喜好修改行为。

然而,这是一个编解码器。

再做一点工作,就可以用其他方式构造类。 我假设有一个默认构造函数来实例它,然后更新类dict。

import json
import collections


class JsonClassSerializable(json.JSONEncoder):

    REGISTERED_CLASS = {}

    def register(ctype):
        JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in self.REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = self.REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


JsonClassSerializable.register(C)


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


JsonClassSerializable.register(B)


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()

JsonClassSerializable.register(A)

A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)

Edit

通过更多的研究,我发现了一种不需要SUPERCLASS寄存器方法调用的泛化方法,使用元类

import json
import collections

REGISTERED_CLASS = {}

class MetaSerializable(type):

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in REGISTERED_CLASS:
            REGISTERED_CLASS[cls.__name__] = cls
        return super(MetaSerializable, cls).__call__(*args, **kwargs)


class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()


A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s

我一直在我的Flask应用程序中使用的一种方法,将类实例序列化为JSON响应。

Github项目供参考

from json import JSONEncoder
import json
from typing import List

class ResponseEncoder(JSONEncoder):
    def default(self, o):
        return o.__dict__

class ListResponse:
    def __init__(self, data: List):
        self.data = data
        self.count = len(data)

class A:
    def __init__(self, message: str):
        self.message = message

class B:
    def __init__(self, record: A):
        self.record = record

class C:
    def __init__(self, data: B):
        self.data = data

现在创建一个A, B, C的实例,然后编码。

data_a = A('Test Data')
data_b = B(data_a)
data_c = C(data_b)

response = ResponseEncoder().encode(data_c)
json_response = json.loads(response)

输出

{
    "data": {
        "record": {
            "message": "Test Data"
        }
    }
}

对于列表类型响应

records = ['One', 'Two', 'Three']
list_response = ListResponse(records)
response = ResponseEncoder().encode(list_response)
json_response = json.loads(response)

输出

{
    "data": [
        "One",
        "Two",
        "Three"
    ],
    "count": 3
}

我认为,与其像公认的答案中建议的那样继承,不如使用多态。否则你必须有一个大的if else语句来自定义每个对象的编码。这意味着为JSON创建一个通用的默认编码器:

def jsonDefEncoder(obj):
   if hasattr(obj, 'jsonEnc'):
      return obj.jsonEnc()
   else: #some default behavior
      return obj.__dict__

然后在你想序列化的每个类中都有一个jsonEnc()函数。如。

class A(object):
   def __init__(self,lengthInFeet):
      self.lengthInFeet=lengthInFeet
   def jsonEnc(self):
      return {'lengthInMeters': lengthInFeet * 0.3 } # each foot is 0.3 meter

然后调用json.dumps(classInstance,default=jsonDefEncoder)

有一种方法非常适合我,你可以试试:

Json.dumps()可以接受一个可选参数default,您可以在其中为未知类型指定一个自定义序列化器函数,在我的例子中是这样的

def serialize(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, date):
        serial = obj.isoformat()
        return serial

    if isinstance(obj, time):
        serial = obj.isoformat()
        return serial

    return obj.__dict__

前两个if用于日期和时间序列化 然后有一个obj。为任何其他对象返回__dict__。

最终决定是这样的:

json.dumps(myObj, default=serialize)

当你在序列化一个集合,并且你不想为每个对象显式地调用__dict__时,它特别好。这里是自动完成的。

到目前为止对我来说很好,期待你的想法。