【问题标题】:How to make a class JSON serializable如何使类 JSON 可序列化
【发布时间】:2011-04-15 16:42:52
【问题描述】:

如何使 Python 类可序列化?

一个简单的类:

class FileItem:
    def __init__(self, fname):
        self.fname = fname

我应该怎么做才能获得以下输出:

>>> import json

>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable

没有错误

【问题讨论】:

  • 不幸的是,答案似乎都回答了“我如何序列化一个类?”这个问题。而不是行动问题“我如何使一个类可序列化?”这些答案假设您自己进行序列化,而不是将对象传递给其他对其进行序列化的模块。
  • 如果你使用 Python3.5+,你可以使用 jsons。它会将您的对象(以及递归的所有属性)转换为字典。 import jsons 见下面的答案 - 它工作得很好
  • @KyleDelaney 我真的希望我可以实现一个接口/魔术方法也可以实现可序列化。我想我必须实现一个.to_dict() 函数或可以在对象上调用的东西,然后再将它传递给尝试序列化它的模块。
  • 查看 stackoverflow.com/a/63718624/1497139 了解 JSONAble mixin
  • 令人惊讶的是,11 年来没有一个回答可以回答这个问题。 OP 声明他想使用json.dumps,但所有的答案,包括获得的赏金,都涉及创建一个自定义编码器,这完全避开了问题的重点。

标签: python json serialization


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

【讨论】:

  • 非常有限。如果你有一个 dict {"foo":"bar","baz":"bat"},它将很容易地序列化为 JSON。如果你有 {"foo":"bar","baz":MyObject()},那么你不能。理想的情况是嵌套对象递归地序列化为 JSON,而不是显式地。
  • 它仍然可以工作。你错过了o.__dict___。试试你自己的例子:class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
  • 此解决方案是否可逆? IE。从json重构对象容易吗?
  • 这不适用于datetime.datetime 实例。它会引发以下错误:'datetime.datetime' object has no attribute '__dict__'
  • 我一定遗漏了一些东西,但这似乎不起作用(即,json.dumps(me) 没有调用ObjecttoJSON 方法。
【解决方案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>
>>> 

【讨论】:

  • 使用__dict__ 并非在所有情况下都有效。如果在实例化对象后尚未设置属性,则__dict__ 可能未完全填充。在上面的例子中,你没问题,但是如果你有你也想编码的类属性,那些将不会在__dict__ 中列出,除非它们已经在类的__init__ 调用中或通过其他方式进行了修改在对象被实例化之后。
  • +1,但是用作对象挂钩的from_json()函数应该有一个else: return json_object语句,所以它也可以处理一般对象。
  • @KrisHardy __dict__ 在新样式类上使用 __slots__ 也不起作用。
  • 您可以使用上面的自定义JSONEncoder来创建自定义协议,例如检查__json_serializable__方法的存在并调用它以获得对象的JSON可序列化表示。这将与其他 Python 模式保持一致,例如 __getitem____str____eq____len__
  • __dict__ 也不会递归工作,例如,如果您的对象的属性是另一个对象。
【解决方案3】:

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

jsonpickle 是一个 Python 库,用于将复杂的 Python 对象与 JSON 进行序列化和反序列化。

用于将 Python 编码为 JSON 的标准 Python 库,例如 stdlib 的 json、simplejson 和 demjson,只能处理具有直接 JSON 等价物的 Python 原语(例如,dicts、lists、strings、ints 等)。 jsonpickle 建立在这些库之上,并允许将更复杂的数据结构序列化为 JSON。 jsonpickle 具有高度可配置性和可扩展性——允许用户选择 JSON 后端并添加额外的后端。

(link to jsonpickle on PyPi)

【讨论】:

  • 来自 C#,这是我所期待的。一个简单的班轮,不会弄乱类。
  • jsonpickle 很棒。它非常适合具有多个级别的类的巨大、复杂、凌乱的对象
  • 是否有将其保存到文件的正确方法的示例?该文档仅显示如何编码和解码 jsonpickle 对象。此外,这无法解码包含熊猫数据帧的字典。
  • @user5359531 你可以使用obj = jsonpickle.decode(file.read())file.write(jsonpickle.encode(obj))
  • 它对我有用!。这是我需要的。我只是想打印一个行为场景对象。
【解决方案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

如果您的类只是基本数据表示,则此方法有效,对于更棘手的事情,您始终可以显式设置键。

【讨论】:

  • 这确实是一个不错的解决方案:) 我相信我的情况是这样。好处:您可以通过使用 init 使其成为类来传达对象的“形状”,它本质上是可序列化的,并且看起来可以解释为 repr
  • 虽然“点访问”仍然缺失:(
  • 啊,这似乎行得通!谢谢,不知道为什么这不是公认的答案。我完全同意更改dumps 不是一个好的解决方案。顺便说一句,在大多数情况下,您可能希望将dict 继承与委托一起使用,这意味着您将在您的类中拥有一些dict 类型属性,然后您将把这个属性作为参数作为初始化传递给@987654325 @.
  • 在我的用例中,我需要将“不可见”的数据存储到 json.dumps(),所以我使用了这种方法。 DictWithRider 类接受任意对象,将其存储为成员,并通过函数 get_rider_obj() 使其可访问,但不会将其传递给 dict.__init__()。因此,想要查看“隐藏”数据的应用程序部分可以调用 d.get_rider_obj() 但 json.dumps() 基本上看到的是一个空字典。正如@PascalVKooten 提到的,您无法使用点符号访问常规成员,但您可以访问函数。
  • 对于简单的使用,这是理想的。点符号可以很容易地通过dict.__init__( 行之后的附加行启用为self.fname = fname,并且可以使用f = FileItem(**json.loads(serialised_f)) 反序列化对象
【解决方案5】:

我喜欢Onur's answer,但会扩展为包含一个可选的toJSON() 方法,以便对象序列化自己:

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

【讨论】:

  • 我发现这是使用现有json.dumps 和引入自定义处理之间的最佳平衡。谢谢!
  • 我真的很喜欢这个;但不是try-catch 可能会做类似if 'toJSON' in obj.__attrs__():... 以避免静默失败(如果toJSON() 由于其他原因而失败,而不是它不存在)...可能导致的失败到数据损坏。
  • @thclark 据我了解,idomatic python 要求宽恕,而不是许可,所以 try-except 是正确的方法,但应该捕获正确的异常,在这种情况下是 AttributeError 。
  • @phil 比你大几岁,现在更聪明了,我同意你的看法。
  • 这真的应该明确地捕捉到AttributeError
【解决方案6】:

只需将to_json 方法添加到您的类中,如下所示:

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

并将此代码 (来自 this answer 添加到所有内容的顶部:

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 模块时对其进行猴子补丁,所以 JSONEncoder.default() 自动检查特殊的 to_json() 方法并在找到时使用它对对象进行编码。

Just like Onur said,但这次您不必更新项目中的每个json.dumps()

【讨论】:

  • 非常感谢!这是唯一能让我做我想做的事的答案:能够在不更改现有代码的情况下序列化对象。其他方法大多对我不起作用。对象是在第三方库中定义的,序列化代码也是第三方的。改变它们会很尴尬。用你的方法,我只需要TheObject.to_json = my_serializer
  • 这是正确答案。我做了一个小改动:import json _fallback = json._default_encoder.default json._default_encoder.default = lambda obj: getattr(obj.__class__, "to_json", _fallback)(obj)
【解决方案7】:

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

json.dumps(obj, default=vars)

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

【讨论】:

  • 不清楚default=vars 是什么意思,这是否意味着vars 是默认的序列化程序?如果不是:这并不能真正解决您无法影响json.dumps 的调用方式的情况。如果您只是将一个对象传递给一个库并且该库在该对象上调用json.dumps,那么如果该库不以这种方式使用dumps,那么您实现vars 并没有真正的帮助。从这个意义上说,它等同于自定义JSONEncoder
  • 您是对的,这只是自定义序列化程序的简单选择,并不能解决您描述的情况。如果我没看错的话,如果您不控制json.dumps 的调用方式,则无法解决此问题。
  • 对于某些对象,这种做法会抛出vars() argument must have __dict__ attribute
  • 这可能是最好的解决方案,干扰最小,并且最容易理解
  • 感谢您,与内置正确定义的库一起使用非常简单。
【解决方案8】:

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

import json

class FileItem:
    def __init__(self, fname):
        self.fname = fname

    def __repr__(self):
        return json.dumps(self.__dict__)

或者,更好的是,从 JsonSerializable 类继承 FileItem 类:

import json

class JsonSerializable(object):
    def toJson(self):
        return json.dumps(self.__dict__)

    def __repr__(self):
        return self.toJson()


class FileItem(JsonSerializable):
    def __init__(self, fname):
        self.fname = fname

测试:

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

【讨论】:

  • 嗨,我不太喜欢这种“自定义编码器”方法,如果你能让你的类 json 可序列化会更好。我试了试,试了试,一无所获。有什么想法如何做到这一点。问题是 json 模块针对内置的 python 类型测试你的类,甚至说自定义类让你的编码器:)。可以伪造吗?所以我可以对我的班级做点什么,让它表现得像 json 模块的简单列表?我尝试了 subclasscheckinstancecheck 但没有。
  • @ADRENALIN 如果所有类属性值都是可序列化的并且您不介意黑客攻击,您可以从主要类型(可能是 dict)继承。您还可以使用 jsonpickle 或 json_tricks 或其他东西代替标准编码器(仍然是自定义编码器,但不需要编写或调用)。前者腌制实例,后者将其存储为属性字典,您可以通过实现__json__encode__ / __json_decode__ 来更改它(披露:我做了最后一个)。
  • 这不会使 json 类的对象可序列化。它只提供了一种方法来获取返回的 json 字符串(微不足道)。因此json.dumps(f) 将失败。这不是我们所要求的。
【解决方案9】:

前几天我遇到了这个问题,并为 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"
}

【讨论】:

  • 虽然这有点老了..我面临一些循环导入错误。所以我没有在最后一行中使用return obj,而是使用了return super(ObjectEncoder, self).default(obj)。参考HERE
【解决方案10】:

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

【讨论】:

  • 如果您能够使用 Python 3.7+,我发现将 python 类转换为 dicts 和 JSON 字符串(反之亦然)的最简洁的解决方案是将 jsons 库与 dataclasses 混合使用。到目前为止,对我来说很好!
  • 这是一个外部库,未内置在标准 Python 安装中。
  • 仅适用于具有 slots 属性的类
  • 可以,但不需要使用 slots。仅当根据特定类的签名进行转储时,您才需要 slots。在即将发布的 1.1.0 版本中,情况也不再如此。
  • 这个库在反序列化/序列化方面都非常慢,至少从个人测试来看是这样。我建议使用其他 ser 库。
【解决方案11】:
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))

【讨论】:

  • 我通过使用 lambda json.dumps(User('alice', 'alice@mail.com'), default=lambda x: x.__dict__) 删除 _asdict 函数来简化这一点
【解决方案12】:

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

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

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

【讨论】:

    【解决方案13】:

    这是我的 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)
    

    【讨论】:

      【解决方案14】:

      这个类可以解决问题,它将对象转换为标准 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.7python3 工作。

      【讨论】:

      • 我最喜欢这种方法。我在尝试序列化成员/方法不可序列化的更复杂对象时遇到了问题。这是适用于更多对象的我的实现: ``` 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 除了 TypeError: o.__dict__[k] = str(v) return o return json.dumps(check(obj).__dict__, indent=2) ``跨度>
      【解决方案15】:
      import json
      
      class Foo(object):
          def __init__(self):
              self.bar = 'baz'
              self._qux = 'flub'
      
          def somemethod(self):
              pass
      
      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)
      

      【讨论】:

      • 来自doc:参数default(obj)是一个函数,它应该返回obj的可序列化版本或引发TypeError。默认的 default 只会引发 TypeError。
      【解决方案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):

      反序列化(JSON -> Python):

      【讨论】:

        【解决方案17】:

        这对我来说效果很好:

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

        【讨论】:

          【解决方案18】:

          “使**可序列化”的真正答案

          _

          TLDR:从下面的选项 2 中复制粘贴代码

          解释:

          • 虽然有一个可行的解决方案,没有 python“官方”解决方案。
            • 我不确定为什么没有其他答案可以解释这一点。
            • 通过官方解决方案,我的意思是(截至 2021 年)无法向您的类添加方法(如 JavaScript 中的 toJSON),也无法使用内置 json 模块注册您的类。执行json.dumps([1,2, your_obj]) 时不会自动调用官方方法/查找。
            • 最接近的官方方法可能是andyhasit's answer,它是从字典继承的。但是,对于许多自定义类(如 AdvancedDateTime 或 pytorch 张量)来说,从字典继承并不是很好。
          • 理想的解决方法是:
            • 变异json.dumps(影响无处不在,甚至是导入 json 的 pip 模块)
            • def __json__(self) 方法添加到您的类中

          _

          选项 1:让a Module 进行修补

          Fancy John's answer的扩展+打包版本,谢谢@FancyJohn)
          pip install json-fix

          some_file_thats_imported_before_your_class_definitions.py

          # Step 1: patch json.dumps, only needs to run once per runtime
          import json_fix
          

          your_class_definition.py

          # Step 2
          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 natually json-able"
          

          _

          选项 2:自己修补 json.dumps

          注意:这种方法被简化了,并且错过了控制 pip 模块(在选项 1 中)的外部类(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
          

          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"
          

          _

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

          • here in the docs 已涵盖其中(搜索“复杂”以获取编码复数的示例)

          【讨论】:

            【解决方案19】:

            如果你不介意为它安装一个包,你可以使用json-tricks:

            pip install json-tricks
            

            之后你只需要从json_tricks导入dump(s)而不是json,它通常会起作用:

            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 中包含 cmets 的能力。

            免责声明:我创建了json_tricks,因为我遇到了和你一样的问题。

            【讨论】:

            • 我刚刚测试了 json_tricks,它可以美化(2019 年)。
            【解决方案20】:

            Kyle Delaney's comment is correct 所以我尝试使用答案https://stackoverflow.com/a/15538391/1497139 以及https://stackoverflow.com/a/10254820/1497139 的改进版本

            创建一个“JSONAble”混合。

            所以要使类 JSON 可序列化,请使用“JSONAble”作为超类并调用:

             instance.toJSON()
            

             instance.asJSON()
            

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

            带有家庭和个人样本的单元测试的测试示例结果:

            toJSOn():

            {
                "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/ 获得

            【讨论】:

              【解决方案21】:

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

              【讨论】:

              • 它是否适用于嵌套对象?包括解码和编码
              【解决方案22】:
              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))
              

              【讨论】:

                【解决方案23】:

                Quinten Caboanswer 为基础:

                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() 调用可能是唯一需要改变的东西)

                【讨论】:

                  【解决方案24】:

                  我最喜欢 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)
                  

                  【讨论】:

                    【解决方案25】:

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

                    苦苦挣扎了一会儿,下面是通用的解决方案。

                    我的解决方案的关键是通过 Python 的源代码并意识到代码文档(描述为 here)已经解释了如何扩展现有的 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 投诉,只需添加代码来处理 unserializable-to-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)

                    有任何问题,请在 cmets 部分发帖。谢谢!

                    【讨论】:

                      【解决方案26】:

                      首先,我们需要使我们的对象符合 JSON 标准,以便我们可以使用标准 JSON 模块转储它。我是这样做的:

                      def serialize(o):
                          if isinstance(o, dict):
                              return {k:serialize(v) for k,v in o.items()}
                          if isinstance(o, list):
                              return [serialize(e) for e in o]
                          if isinstance(o, bytes):
                              return o.decode("utf-8")
                          return o
                      

                      【讨论】:

                        【解决方案27】:

                        此函数使用递归遍历字典的每个部分,然后调用非内置类型的类的 repr() 方法。

                        def sterilize(obj):
                            object_type = type(obj)
                            if isinstance(obj, dict):
                                return {k: sterilize(v) for k, v in obj.items()}
                            elif object_type in (list, tuple):
                                return [sterilize(v) for v in obj]
                            elif object_type in (str, int, bool, float):
                                return obj
                            else:
                                return obj.__repr__()
                        

                        【讨论】:

                          【解决方案28】:

                          要在这 11 年的大火上再扔一个日志,我想要一个满足以下条件的解决方案:

                          • 允许仅使用json.dumps(obj) 序列化 FileItem 类的实例
                          • 允许 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)它的库。

                          【讨论】:

                            【解决方案29】:

                            我想出了自己的解决方案。使用此方法,传递任意文档(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
                            

                            【讨论】:

                              【解决方案30】:

                              如果您能够安装软件包,我建议您尝试dill,它对我的​​项目非常有效。这个包的一个好处是它和pickle有相同的接口,所以如果你已经在你的项目中使用了pickle,你可以简单地替换dill并查看脚本是否运行,而无需更改任何代码.因此,这是一个非常便宜的解决方案!

                              (完全反披露:我与 dill 项目没有任何关联,也从未参与过。)

                              安装包:

                              pip install dill
                              

                              然后编辑您的代码以导入dill 而不是pickle

                              # import pickle
                              import dill as pickle
                              

                              运行你的脚本,看看它是否有效。 (如果是这样,您可能需要清理您的代码,以便您不再隐藏 pickle 模块名称!)

                              dill 可以和不能序列化的数据类型的一些细节,来自the project page

                              dill 可以腌制以下标准类型:

                              无、类型、布尔值、int、long、float、complex、str、unicode、tuple、 list、dict、file、buffer、builtin、新旧样式类, 新旧样式类的实例,集合,frozenset,数组, 函数,异常

                              dill 还可以腌制更多“异国情调”的标准类型:

                              具有产量的函数、嵌套函数、lambda、单元格、方法、 unboundmethod,模块,代码,methodwrapper,dictproxy, 方法描述符,getsetdescriptor,成员描述符, wrapperdescriptor, xrange, slice, notimplemented, ellipsis, quit

                              dill 还不能腌制这些标准类型:

                              框架、生成器、回溯

                              【讨论】:

                                猜你喜欢
                                • 2019-11-05
                                • 2020-07-18
                                • 2019-11-22
                                • 2020-04-22
                                • 1970-01-01
                                • 2015-12-21
                                • 1970-01-01
                                相关资源
                                最近更新 更多