如何使类 JSON 可序列化

2024-11-20 08:44:00
admin
原创
8
摘要:问题描述:如何使 Python 类可序列化?class FileItem: def __init__(self, fname): self.fname = fname 尝试序列化为 JSON:>>> import json >>> x = FileI...

问题描述:

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

解决方案 1:

这是一个针对简单功能的简单解决方案:

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

对于功能齐全的库,您可以使用orjson

解决方案 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"}'

然后将此类json.dumps()作为clskwarg 传递到方法中:

json.dumps(cls=MyEncoder)

如果您还想解码,那么您必须object_hook为该类提供自定义JSONDecoder。例如:

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

解决方案 3:

对于更复杂的类,你可以考虑使用工具jsonpickle

jsonpickle 是一个 Python 库,用于将复杂的 Python 对象序列化为 JSON 或从 JSON 反序列化。

用于将 Python 编码为 JSON 的标准 Python 库(例如 stdlib 的 json、simplejson 和 demjson)只能处理具有直接 JSON 等效项的 Python 原语(例如字典、列表、字符串、整数等)。jsonpickle 建立在这些库之上,并允许将更复杂的数据结构序列化为 JSON。jsonpickle 具有高度可配置性和可扩展性 - 允许用户选择 JSON 后端并添加其他后端。

将对象转换为 JSON 字符串:

import jsonpickle
json_string = jsonpickle.encode(obj)

从 JSON 字符串重新创建 Python 对象:

recreated_obj = jsonpickle.decode(json_string)

(PyPi 上的 jsonpickle 链接)

解决方案 4:

大多数答案都涉及将调用更改为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

如果您的类只是基本的数据表示,那么这种方法是可行的,对于比较棘手的事情,您总是可以在调用时明确设置键dict.__init__()

之所以有效是因为它json.dumps()通过一种相当非 Python 的方式检查对象是否是几种已知类型之一- 所以如果你真的不想从继承的话,isinstance(value, dict)就可以用这种方法和其他一些方法来伪造它。__class__`dict`

解决方案 5:

正如许多其他答案中提到的那样,你可以传递一个函数来json.dumps将默认不支持的类型的对象转换为支持的类型。令人惊讶的是,他们都没有提到最简单的情况,即使用内置函数vars将对象转换为包含其所有属性的字典:

json.dumps(obj, default=vars)

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

解决方案 6:

只需to_json像这样向您的课程添加方法:

def to_json(self):
  return self.message # or how you want it to be serialized

并将此代码(来自此答案)添加到所有内容顶部的某处:

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder().default
JSONEncoder.default = _default

在导入时,这将对 json 模块进行 monkey-patch,因此会
JSONEncoder.default()自动检查特殊to_json()
方法,并在找到后使用它对对象进行编码。

就像 Onur 所说的那样,但这次您不必更新json.dumps()项目中的每一个。

解决方案 7:

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

所以你想让 Python 的json模块与你的类一起工作吗?

  • 好消息:是的,有一个可靠的解决方案

  • 坏消息:没有 Python“官方”解决方案

    • 官方解决方案是指,截至 2024 年,没有办法(如toJSONJavaScript 中那样)向您的类添加方法,并且/或者没有办法使用内置 json 模块注册您的类。json.dumps([1,2, your_obj])执行类似操作时,python 不会检查查找表或对象方法。

    • 我不确定为什么其他答案没有解释这一点

    • 最接近的官方方法可能是andyhasit 的答案,即从字典继承。但是,对于许多自定义类(如 AdvancedDateTime 或 pytorch 张量),从字典继承效果不佳。

  • 理想的解决方法是这样的:

    • def __json__(self)向您的班级添加方法

    • 变异json.dumps以检查__json__方法(影响所有地方,甚至影响导入 json 的 pip 模块)

    • 注意:修改内置内容通常不是很好,但是这种更改应该没有副作用,即使它被不同的代码库多次应用。它在运行时是完全可逆的(如果模块想要撤消修改)。无论好坏,这是目前能做的最好的事情。

选项 1:让模块进行修补

pip install json-fix
( Fancy John 答案
的扩展+打包版本,感谢@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)
# '[
1,
2,
"a built-in object that is naturally json-able"
]'

为了使json.dumpsNumpy 数组、Pandas DataFrames 和其他第三方对象能够工作,请参阅模块(只有约 2 行代码,但需要解释)。

它是如何工作的?嗯...

选项 2:自己修补 json.dumps

注意:这种方法是简化的,它在已知的边缘情况(例如:如果你的自定义类继承自dict或另一个内置类)上会失败,并且它错过了控制外部类(numpy 数组、日期时间、数据框、张量等)的 json 行为。

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

你的类定义.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”可以找到编码复数的示例)

解决方案 8:

我喜欢Onur 的答案,但会扩展以包含一个toJSON()用于对象自身序列化的可选方法:

def dumper(obj):
    try:
        return obj.toJSON()
    except:
        return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)

解决方案 9:

如果您使用的是 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

解决方案 10:

另一种选择是将 JSON 转储包装在其自己的类中:

import json

class FileItem:
    def __init__(self, fname: str) -> None:
        self.fname = fname

    def __repr__(self) -> str:
        return json.dumps(self.__dict__)

或者,更好的是,从JsonSerializable协议类中子类化 FileItem 类:

import json
from typing import Protocol

class JsonSerializable(Protocol):
    def to_json(self) -> str:
        return json.dumps(self.__dict__)

    def __repr__(self) -> str:
        return self.to_json()


class FileItem(JsonSerializable):
    def __init__(self, fname: str) -> None:
        self.fname = fname

测试:

>>> f = FileItem('/foo/bar')
>>> f.to_json()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'

解决方案 11:

前几天我遇到了这个问题,并实现了一个更通用的 Python 对象编码器版本,可以处理嵌套对象继承字段

import json
import inspect

class ObjectEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, "to_json"):
            return self.default(obj.to_json())
        elif hasattr(obj, "__dict__"):
            d = dict(
                (key, value)
                for key, value in inspect.getmembers(obj)
                if not key.startswith("__")
                and not inspect.isabstract(value)
                and not inspect.isbuiltin(value)
                and not inspect.isfunction(value)
                and not inspect.isgenerator(value)
                and not inspect.isgeneratorfunction(value)
                and not inspect.ismethod(value)
                and not inspect.ismethoddescriptor(value)
                and not inspect.isroutine(value)
            )
            return self.default(d)
        return obj

例子:

class C(object):
    c = "NO"
    def to_json(self):
        return {"c": "YES"}

class B(object):
    b = "B"
    i = "I"
    def __init__(self, y):
        self.y = y
        
    def f(self):
        print "f"

class A(B):
    a = "A"
    def __init__(self):
        self.b = [{"ab": B("y")}]
        self.c = C()

print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)

结果:

{
  "a": "A", 
  "b": [
    {
      "ab": {
        "b": "B", 
        "i": "I", 
        "y": "y"
      }
    }
  ], 
  "c": {
    "c": "YES"
  }, 
  "i": "I"
}

解决方案 12:

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

import json

json.dumps(your_object, default=vars)

完结!

下面是一个测试。

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

输出:

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

解决方案 13:

import simplejson

class User(object):
    def __init__(self, name, mail):
        self.name = name
        self.mail = mail

    def _asdict(self):
        return self.__dict__

print(simplejson.dumps(User('alice', 'alice@mail.com')))

如果使用标准json,则需要定义一个default函数

import json
def default(o):
    return o._asdict()

print(json.dumps(User('alice', 'alice@mail.com'), default=default))

解决方案 14:

最简单的答案

class Object(dict):
    def __init__(self):
        pass

    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        self[key] = value

# test
obj = Object()
obj.name = "John"
obj.age = 25
obj.brothers = [ Object() ]
text = json.dumps(obj)

现在它会给你输出,不要将任何内容更改为 json.dumps(...)

'{"name": "John", "age": 25, "brothers": [{}]}'

解决方案 15:

json在可以打印的对象方面受到限制,并且jsonpickle(您可能需要pip install jsonpickle)在无法缩进文本方面受到限制。如果您想检查无法更改其类的对象的内容,我仍然找不到比以下更直接的方法:

 import json
 import jsonpickle
 ...
 print  json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

注意:他们仍然无法打印对象方法。

解决方案 16:

jaraco给出了一个非常简洁的答案。我需要修复一些小问题,但这有效:

代码

# Your custom class
class MyCustom(object):
    def __json__(self):
        return {
            'a': self.a,
            'b': self.b,
            '__python__': 'mymodule.submodule:MyCustom.from_json',
        }

    to_json = __json__  # supported by simplejson

    @classmethod
    def from_json(cls, json):
        obj = cls()
        obj.a = json['a']
        obj.b = json['b']
        return obj

# Dumping and loading
import simplejson

obj = MyCustom()
obj.a = 3
obj.b = 4

json = simplejson.dumps(obj, for_json=True)

# Two-step loading
obj2_dict = simplejson.loads(json)
obj2 = MyCustom.from_json(obj2_dict)

# Make sure we have the correct thing
assert isinstance(obj2, MyCustom)
assert obj2.__dict__ == obj.__dict__

注意,加载需要两个步骤。目前,该__python__属性尚未使用。

这种现象有多常见?

使用AlJohri的方法,我检查了方法的流行度:

序列化(Python -> JSON):

  • to_json:2018-06-27 共 266,595 人

  • toJSON: 2018-06-27 共 96,307 人

  • __json__:2018-06-27 上 8,504

  • for_json: 2018-06-27 共 6,937 人

反序列化(JSON -> Python):

  • from_json: 2018-06-27 共 226,101 人

解决方案 17:

为了给这场持续了 11 年的大火再添一笔,我想要一个满足以下标准的解决方案:

  • 允许仅使用以下方法序列化 FileItem 类的实例json.dumps(obj)

  • 允许 FileItem 实例具有属性:fileItem.fname

  • 允许将 FileItem 实例提供给任何可以使用以下方法序列化的库json.dumps(obj)

  • 不需要传递任何其他字段json.dumps(例如自定义序列化器)

IE:

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

我的解决方案是:

  • 让 obj 的类继承自dict

  • 将每个对象属性映射到底层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)它的库。

解决方案 18:

这是我的 3 分...

这演示了树状 python 对象的显式 json 序列化。

注意:如果您确实想要这样的代码,则可以使用twisted FilePath类。

import json, sys, os

class File:
    def __init__(self, path):
        self.path = path

    def isdir(self):
        return os.path.isdir(self.path)

    def isfile(self):
        return os.path.isfile(self.path)

    def children(self):        
        return [File(os.path.join(self.path, f)) 
                for f in os.listdir(self.path)]

    def getsize(self):        
        return os.path.getsize(self.path)

    def getModificationTime(self):
        return os.path.getmtime(self.path)

def _default(o):
    d = {}
    d['path'] = o.path
    d['isFile'] = o.isfile()
    d['isDir'] = o.isdir()
    d['mtime'] = int(o.getModificationTime())
    d['size'] = o.getsize() if o.isfile() else 0
    if o.isdir(): d['children'] = o.children()
    return d

folder = os.path.abspath('.')
json.dump(File(folder), sys.stdout, default=_default)

解决方案 19:

这个类可以达到这个目的,它将对象转换为标准 json 。

import json


class Serializer(object):
    @staticmethod
    def serialize(object):
        return json.dumps(object, default=lambda o: o.__dict__.values()[0])

用法:

Serializer.serialize(my_object)

python2.7在和工作python3

解决方案 20:

你们为什么要搞得这么复杂?这是一个简单的例子:

#!/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,因为default带有简单 lambda 的参数也可以正常工作。

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

解决方案 21:

这对我来说很有效:

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

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

解决方案 22:

import json

class Foo(object):
    def __init__(self):
        self.bar = 'baz'
        self._qux = 'flub'

    def somemethod(self):
        pass

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

https://docs.python.org/3.4/library/json.html#json.dumps
'''
def default(instance):
    return {k: v
            for k, v in vars(instance).items()
            if not str(k).startswith('_')}

json_foo = json.dumps(Foo(), default=default)
assert '{"bar": "baz"}' == json_foo

print(json_foo)

解决方案 23:

如果您不介意为其安装包,则可以使用json-tricks:

pip install json-tricks

之后,您只需要从 json 中导入dump(s)json_tricks它通常就可以工作:

from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)

这将给出

{
        "__instance_type__": [
                "module_name.test_class",
                "MyTestCls"
        ],
        "attributes": {
                "attr": "val",
                "dct_attr": {
                        "hello": 42
                }
        }
}

基本上就是这样!


总体来说,这很有效。但也有一些例外,例如,如果发生了特殊情况__new__,或者发生了更多元类魔法。

显然加载也有效(否则还有什么意义):

from json_tricks import loads
json_str = loads(json_str)

这确实假设module_name.test_class.MyTestCls可以导入并且没有以不兼容的方式更改。您将返回一个实例,而不是一些字典或类似的东西,并且它应该是与您转储的那个相同的副本。

如果你想要自定义某些东西如何被序列化(反序列化),你可以向你的类中添加特殊方法,就像这样:

class CustomEncodeCls:
        def __init__(self):
                self.relevant = 42
                self.irrelevant = 37

        def __json_encode__(self):
                # should return primitive, serializable types like dict, list, int, string, float...
                return {'relevant': self.relevant}

        def __json_decode__(self, **attrs):
                # should initialize all properties; note that __init__ is not called implicitly
                self.relevant = attrs['relevant']
                self.irrelevant = 12

作为示例,它仅序列化部分属性参数。

作为免费奖励,您可以获得 numpy 数组、日期和时间、有序映射的序列化(反序列化),以及在 json 中包含注释的能力。

免责声明:我创建了json_tricks,因为我遇到了与您相同的问题。

解决方案 24:

Kyle Delaney 的评论是正确的,所以我尝试使用答案https://stackoverflow.com/a/15538391/1497139以及https://stackoverflow.com/a/10254820/1497139的改进版本

创建一个“JSONAble”混合类。

因此,要使类 JSON 可序列化,请使用“JSONAble”作为超类,然后调用:

 instance.toJSON()

或者

 instance.asJSON()

对于提供的两种方法。您还可以使用此处提供的其他方法扩展 JSONAble 类。

包含家庭和人员样本的单元测试示例结果如下:

到JSOn():

{
    "members": {
        "Flintstone,Fred": {
            "firstName": "Fred",
            "lastName": "Flintstone"
        },
        "Flintstone,Wilma": {
            "firstName": "Wilma",
            "lastName": "Flintstone"
        }
    },
    "name": "The Flintstones"
}

asJSOn():

{'name': 'The Flintstones', 'members': {'Flintstone,Fred': {'firstName': 'Fred', 'lastName': 'Flintstone'}, 'Flintstone,Wilma': {'firstName': 'Wilma', 'lastName': 'Flintstone'}}}

包含家庭和个人示例的单元测试

def testJsonAble(self):
        family=Family("The Flintstones")
        family.add(Person("Fred","Flintstone")) 
        family.add(Person("Wilma","Flintstone"))
        json1=family.toJSON()
        json2=family.asJSON()
        print(json1)
        print(json2)

class Family(JSONAble):
    def __init__(self,name):
        self.name=name
        self.members={}
    
    def add(self,person):
        self.members[person.lastName+","+person.firstName]=person

class Person(JSONAble):
    def __init__(self,firstName,lastName):
        self.firstName=firstName;
        self.lastName=lastName;

jsonable.py 定义 JSONAble mixin

 '''
Created on 2020-09-03

@author: wf
'''
import json

class JSONAble(object):
    '''
    mixin to allow classes to be JSON serializable see
    https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable
    '''

    def __init__(self):
        '''
        Constructor
        '''
    
    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, 
            sort_keys=True, indent=4)
        
    def getValue(self,v):
        if (hasattr(v, "asJSON")):
            return v.asJSON()
        elif type(v) is dict:
            return self.reprDict(v)
        elif type(v) is list:
            vlist=[]
            for vitem in v:
                vlist.append(self.getValue(vitem))
            return vlist
        else:   
            return v
    
    def reprDict(self,srcDict):
        '''
        get my dict elements
        '''
        d = dict()
        for a, v in srcDict.items():
            d[a]=self.getValue(v)
        return d
    
    def asJSON(self):
        '''
        recursively return my dict elements
        '''
        return self.reprDict(self.__dict__)   

您会发现这些方法现已集成在https://github.com/WolfgangFahl/pyLoDStorage项目中,该项目可在https://pypi.org/project/pylodstorage/上找到

解决方案 25:

jsonweb 似乎是我的最佳解决方案。请参阅http://www.jsonweb.info/en/latest/

from jsonweb.encode import to_object, dumper

@to_object()
class DataModel(object):
  def __init__(self, id, value):
   self.id = id
   self.value = value

>>> data = DataModel(5, "foo")
>>> dumper(data)
'{"__type__": "DataModel", "id": 5, "value": "foo"}'

解决方案 26:

我想到了自己的解决方案。使用此方法,传递任何文档(dictlistObjectId等)进行序列化。

def getSerializable(doc):
    # check if it's a list
    if isinstance(doc, list):
        for i, val in enumerate(doc):
            doc[i] = getSerializable(doc[i])
        return doc

    # check if it's a dict
    if isinstance(doc, dict):
        for key in doc.keys():
            doc[key] = getSerializable(doc[key])
        return doc

    # Process ObjectId
    if isinstance(doc, ObjectId):
        doc = str(doc)
        return doc

    # Use any other custom serializting stuff here...

    # For the rest of stuff
    return doc

解决方案 27:

class DObject(json.JSONEncoder):
    def delete_not_related_keys(self, _dict):
        for key in ["skipkeys", "ensure_ascii", "check_circular", "allow_nan", "sort_keys", "indent"]:
            try:
                del _dict[key]
            except:
                continue

    def default(self, o):
        if hasattr(o, '__dict__'):
            my_dict = o.__dict__.copy()
            self.delete_not_related_keys(my_dict)
            return my_dict
        else:
            return o

a = DObject()
a.name = 'abdul wahid'
b = DObject()
b.name = a

print(json.dumps(b, cls=DObject))

解决方案 28:

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

不同之处在于

  1. 适用于任何可迭代对象,而不仅仅是listtuple它适用于 NumPy 数组等)

  2. 适用于动态类型(包含 的类型__dict__)。

  3. 包括本机类型floatNone因此它们不会转换为字符串。

  4. __dict__具有和成员的类大部分都可以工作(如果__dict__和成员名称冲突,则只会得到一个 - 可能是成员)

  5. 具有成员的列表类看起来像列表和字典的元组

  6. Python3(该isinstance()调用可能是唯一需要更改的内容)

解决方案 29:

我最喜欢 Lost Koder 的方法。当我尝试序列化成员/方法不可序列化的更复杂对象时,我遇到了问题。这是我对更多对象有效的实现:

class Serializer(object):
    @staticmethod
    def serialize(obj):
        def check(o):
            for k, v in o.__dict__.items():
                try:
                    _ = json.dumps(v)
                    o.__dict__[k] = v
                except TypeError:
                    o.__dict__[k] = str(v)
            return o
        return json.dumps(check(obj).__dict__, indent=2)

解决方案 30:

当我尝试将 Peewee 的模型存储到 PostgreSQL 中时遇到了这个问题JSONField

经过一段时间的努力,这里是通用的解决方案。

我的解决方案的关键是查看 Python 的源代码并意识到代码文档(此处描述)已经解释了如何扩展现有代码json.dumps以支持其他数据类型。

假设您当前有一个模型,其中包含一些不能序列化为 JSON 的字段,并且包含 JSON 字段的模型最初如下所示:

class SomeClass(Model):
    json_field = JSONField()

只需定义JSONEncoder这样的自定义:

class CustomJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
            return < whatever value you want >
        return json.JSONEncoder.default(self, obj)

    @staticmethod
    def json_dumper(obj):
        return json.dumps(obj, cls=CustomJsonEncoder)

然后像JSONField下面这样使用它:

class SomeClass(Model):
    json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)

关键是default(self, obj)上面的方法。对于你收到的来自 Python 的每一个... is not JSON serializable投诉,只需添加代码来处理不可序列化为 JSON 的类型(例如Enumdatetime

例如,下面是我支持从 继承的类的方法Enum

class TransactionType(Enum):
   CURRENT = 1
   STACKED = 2

   def default(self, obj):
       if isinstance(obj, TransactionType):
           return obj.value
       return json.JSONEncoder.default(self, obj)

最后,通过如上实现的代码,您可以将任何 Peewee 模型转换为 JSON 可序列化对象,如下所示:

peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)

虽然上面的代码(在某种程度上)特定于 Peewee,但我认为:

  1. 它通常适用于其他 ORM(Django 等)

  2. 另外,如果你理解了它json.dumps的工作原理,这个解决方案也适用于 Python(无 ORM)

如有任何问题,请在评论部分发表。谢谢!

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用