如何让dict子类json序列化?

mwg9r5ms  于 2023-10-21  发布在  其他
关注(0)|答案(2)|浏览(88)

我可以用json.dumps表示my Simple_Dict_SubclassList_Subclass,但不能用Custom_Dict_Subclass。当json.dumpsList_Subclass上被调用时,它的__iter__方法也被调用,所以我推断json.dumps将调用字典的items方法。itemsSimple_Dict_Subclass中被调用,而不是Custom_Dict_Subclass。如何使Custom_Dict_SubclassjsonSimple_Dict_Subclass一样可序列化?

import json

class Custom_Dict_Subclass(dict):
    def __init__(self):
        self.data = {}

    def __setitem__(self, key, value):
        self.data[key] = value

    def __getitem__(self, key):
        return self.data[key]

    def __str__(self):
        return str(self.data)

    def items(self):
        print("'Items' called from Custom_Dict_Subclass")
        yield from self.data.items()

class Simple_Dict_Subclass(dict):
    def __setitem__(self, key, value):
        super().__setitem__(key, value)

    def __getitem__(self, key):
        return super().__getitem__(key)

    def __str__(self):
        return super().__str__()

    def items(self):
        print("'Items' called from Simple_Dict_Subclass")
        yield from super().items()

class List_Subclass(list):
    def __init__(self):
        self.data = []

    def __setitem__(self, index, value):
        self.data[index] = value

    def __getitem__(self, index):
        return self.data[index]

    def __str__(self):
        return str(self.data)

    def __iter__(self):
        yield from self.data

    def append(self, value):
        self.data.append(value)

d = Custom_Dict_Subclass()
d[0] = None
print(d)             # Works
print(json.dumps(d)) # Does't work

d = Simple_Dict_Subclass()
d[0] = None
print(d)             # Works
print(json.dumps(d)) # Works

l = List_Subclass()
l.append(None)
print(l)             # Works
print(json.dumps(l)) # Works

输出量:

{0: None}   # Custom dict string     working
{}          # Custom dict json.dumps not working

{0: None}   # Simple dict string     working
'Items' called from Simple_Dict_Subclass
{"0": null} # Simple dict json.dumps working

[None]      # List string            working
[null]      # List json.dumps        working
7y4bm7vi

7y4bm7vi1#

一般来说,假设json.dumps将触发字典的items方法是不安全的。这就是它是如何实现的,但你不能依赖它。
在本例中,Custom_Dict_Subclass.items永远不会被调用,因为(键,值)对没有添加到dict对象中,而是添加到它的data属性中。
要解决这个问题,您需要调用Custom_Dict_Subclass中的超级方法:

class Custom_Dict_Subclass(dict):
    def __init__(self):
        dict.__init__(self)
        self.data = {}
    def __setitem__(self, key, value):
        self.data[key] = value
        super().__setitem__(key, value)

对象被正确转储,但当然,(key,value)将被存储两次:在dict对象及其data属性中。
在这种情况下,最好定义一个json.JSONEncoder的子类来实现Custom_Dict_Subclass对象到json可序列化对象的转换,并给予这个类作为json.dumps的关键字参数cls

import json

class Custom_Dict_Subclass:
    def __init__(self):
        self.data = {}

    def __setitem__(self, key, value):
        self.data[key] = value

    def __getitem__(self, key):
        return self.data[key]

    def __str__(self):
        return str(self.data)

    def items(self):
        print("'Items' called from Custom_Dict_Subclass")
        yield from self.data.items()

class CustomDictEncoder(json.JSONEncoder):
    def default(self, obj):
        """called by json.dumps to translate an object obj to
        a json serialisable data"""
        if isinstance(obj, Custom_Dict_Subclass):
            return obj.data
        return json.JSONEncoder.default(self, obj)  

d = Custom_Dict_Subclass()
d[0] = None
print(json.dumps(d, cls=CustomDictEncoder))
szqfcxe2

szqfcxe22#

使用自定义json.JSONEncoder的建议解决方案是实用的,但实际上只是围绕cpython中IMHO看起来像一个bug的问题工作。你应该能够子类化dict,对吗?
但是C优化的JSON编码器似乎不知道这一点。我们找到这个代码:

if (PyDict_GET_SIZE(dct) == 0)  /* Fast path */
        return _PyUnicodeWriter_WriteASCIIString(writer, "{}", 2);

PyDict_GET_SIZE直接从不受您直接控制的原生dict结构中读取。如果你想要一个完全自定义的存储,就像在你的Custom_Dict_Subclass中一样,那么你似乎运气不好,至少从Python 3.12开始是这样。(顺便说一句,cpython提供的OrderedDict子类可以正常工作,因为它通过super使用本机存储。
如果性能不是问题,您可以简单地禁用基于C的JSON编码器:json.encoder.c_make_encoder = None

相关问题