我试图创建一个类实例的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,复数;它还允许注释。

其他回答

使用jsonpickle

import jsonpickle

object = YourClass()
json_object = jsonpickle.encode(object)

这可以用pydantic轻松处理,因为它已经内置了这个功能。

选项1:正常方式

from pydantic import BaseModel

class testclass(BaseModel):
    value1: str = "a"
    value2: str = "b"

test = testclass()

>>> print(test.json(indent=4))
{
    "value1": "a",
    "value2": "b"
}

选项2:使用pydantic的数据类

import json
from pydantic.dataclasses import dataclass
from pydantic.json import pydantic_encoder

@dataclass
class testclass:
    value1: str = "a"
    value2: str = "b"

test = testclass()
>>> print(json.dumps(test, indent=4, default=pydantic_encoder))
{
    "value1": "a",
    "value2": "b"
}

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

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)

基本的问题是JSON编码器JSON .dumps()默认情况下只知道如何序列化有限的对象类型集,所有的内置类型。名单在这里:https://docs.python.org/3.3/library/json.html#encoders-and-decoders

一个好的解决方案是让您的类继承自JSONEncoder,然后实现JSONEncoder.default()函数,并使该函数为您的类发出正确的JSON。

一个简单的解决方案是在该实例的.__dict__成员上调用json.dumps()。这是一个标准的Python字典,如果你的类很简单,它将是JSON序列化的。

class Foo(object):
    def __init__(self):
        self.x = 1
        self.y = 2

foo = Foo()
s = json.dumps(foo) # raises TypeError with "is not JSON serializable"

s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2}

上述方法在这篇博文中进行了讨论:

使用_dict_将任意Python对象序列化为JSON

当然,Python提供了一个内置函数,为您访问.__dict__,称为vars()。

所以上面的例子也可以这样做:

s = json.dumps(vars(foo)) # s set to: {"x":1, "y":2}

你可以在json.dumps()函数中指定默认的命名参数:

json.dumps(obj, default=lambda x: x.__dict__)

解释:

从文档(2.7,3.6):

``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.

(适用于Python 2.7和Python 3.x)

注意:在这种情况下,您需要实例变量而不是类变量,正如问题中的示例所尝试做的那样。(我假设请求者意味着类实例是类的对象)

我首先从@phihag的回答中了解到这一点。发现这是最简单、最干净的工作方式。