如何让dict子类json序列化?

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

我可以用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一样可序列化?

  1. import json
  2. class Custom_Dict_Subclass(dict):
  3. def __init__(self):
  4. self.data = {}
  5. def __setitem__(self, key, value):
  6. self.data[key] = value
  7. def __getitem__(self, key):
  8. return self.data[key]
  9. def __str__(self):
  10. return str(self.data)
  11. def items(self):
  12. print("'Items' called from Custom_Dict_Subclass")
  13. yield from self.data.items()
  14. class Simple_Dict_Subclass(dict):
  15. def __setitem__(self, key, value):
  16. super().__setitem__(key, value)
  17. def __getitem__(self, key):
  18. return super().__getitem__(key)
  19. def __str__(self):
  20. return super().__str__()
  21. def items(self):
  22. print("'Items' called from Simple_Dict_Subclass")
  23. yield from super().items()
  24. class List_Subclass(list):
  25. def __init__(self):
  26. self.data = []
  27. def __setitem__(self, index, value):
  28. self.data[index] = value
  29. def __getitem__(self, index):
  30. return self.data[index]
  31. def __str__(self):
  32. return str(self.data)
  33. def __iter__(self):
  34. yield from self.data
  35. def append(self, value):
  36. self.data.append(value)
  37. d = Custom_Dict_Subclass()
  38. d[0] = None
  39. print(d) # Works
  40. print(json.dumps(d)) # Does't work
  41. d = Simple_Dict_Subclass()
  42. d[0] = None
  43. print(d) # Works
  44. print(json.dumps(d)) # Works
  45. l = List_Subclass()
  46. l.append(None)
  47. print(l) # Works
  48. print(json.dumps(l)) # Works

输出量:

  1. {0: None} # Custom dict string working
  2. {} # Custom dict json.dumps not working
  3. {0: None} # Simple dict string working
  4. 'Items' called from Simple_Dict_Subclass
  5. {"0": null} # Simple dict json.dumps working
  6. [None] # List string working
  7. [null] # List json.dumps working
7y4bm7vi

7y4bm7vi1#

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

  1. class Custom_Dict_Subclass(dict):
  2. def __init__(self):
  3. dict.__init__(self)
  4. self.data = {}
  5. def __setitem__(self, key, value):
  6. self.data[key] = value
  7. super().__setitem__(key, value)

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

  1. import json
  2. class Custom_Dict_Subclass:
  3. def __init__(self):
  4. self.data = {}
  5. def __setitem__(self, key, value):
  6. self.data[key] = value
  7. def __getitem__(self, key):
  8. return self.data[key]
  9. def __str__(self):
  10. return str(self.data)
  11. def items(self):
  12. print("'Items' called from Custom_Dict_Subclass")
  13. yield from self.data.items()
  14. class CustomDictEncoder(json.JSONEncoder):
  15. def default(self, obj):
  16. """called by json.dumps to translate an object obj to
  17. a json serialisable data"""
  18. if isinstance(obj, Custom_Dict_Subclass):
  19. return obj.data
  20. return json.JSONEncoder.default(self, obj)
  21. d = Custom_Dict_Subclass()
  22. d[0] = None
  23. print(json.dumps(d, cls=CustomDictEncoder))
展开查看全部
szqfcxe2

szqfcxe22#

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

  1. if (PyDict_GET_SIZE(dct) == 0) /* Fast path */
  2. 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

相关问题