python - 我怎样才能让 json.dumps 将我的课程视为字典?
问题描述
我想创建一个自定义 Python 类,它像字典一样进行 JSON 序列化。以Python 的duck-typing 命名,我想我可以创建一个看起来和嘎嘎声完全像字典的类。但是,下面显示的类显然不够像 dict json.dumps
- 下面的代码会产生错误TypeError: Object of type TotallyADict is not JSON serializable
。我可以对 TotallyADict 进行哪些更改,以便json.dumps
输出的默认编码器{"a": 1, "b": 2, "c": 3}
?
我知道这个直接的问题可以通过创建自定义编码器来解决,但是在这个特定问题已经从中提炼出来的更大问题中,这不是一个可接受的解决方案。
另一个尝试的解决方案是让 TotallyADict 继承自 dict 而不是 MutableMapping。这不会引发任何异常,但在这种情况下会json.dumps(x)
产生{}
;json.dumps
显然,用于 dicts的默认编码器的数据源不是以下任何被覆盖的方法。
我在这里想要的是能够使用属性语义(x.c = x.a + x.b
)但仍然序列化为 JSON 对象。因此,一个似乎不起作用的可能建议是 TypedDict (必须是x['c'] = x['a'] + x['b']
)。__setattr__
通过and拦截属性分配和检索并重定向到继承自__getattribute__
的条目似乎工作得很好,所以这是我的默认解决方案。但令我惊讶的是,有一次我真的想使用鸭式打字而不是严格(ish)打字,它似乎不起作用。self
dict
from collections.abc import MutableMapping
import json
class TotallyADict(MutableMapping):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
self._fields = {'a', 'b', 'c'}
def __getitem__(self, key):
if key in self._fields:
return getattr(self, key)
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def __setitem__(self, key, value):
if key in self._fields:
setattr(self, key, value)
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def __delitem__(self, key):
raise RuntimeError('Cannot delete fields from {}'.format(type(self).__name__))
def __iter__(self):
return iter(self._fields)
def __len__(self):
return len(self._fields)
def __contains__(self, k):
return k in self._fields
def copy(self):
return type(self)(**{k: getattr(self, k) for k in self._fields})
def __repr__(self):
return '{' + ', '.join('"{}": {}'.format(k, repr(getattr(self, k))) for k in self._fields) + '}'
def get(self, key, default=None):
if key in self._fields:
value = getattr(self, key)
if value is None:
value = default
return value
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def setdefault(self, key, default=None):
if key in self._fields:
value = getattr(self, key)
if value is None:
value = default
setattr(self, key, value)
return value
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def pop(self, key, value=None):
raise RuntimeError('Cannot delete fields from {}'.format(type(self).__name__))
def keys(self):
return self._fields
def items(self):
return [(k, getattr(self, k)) for k in self._fields]
def values(self):
return [getattr(self, k) for k in self._fields]
def __eq__(self, other):
if type(self) is type(other):
for k in self._fields:
if getattr(self, k) != getattr(other, k):
return False
return True
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
x = TotallyADict(1, 2, 3)
print(json.dumps(x))
解决方案
这里的问题是你的_fields
变量。这不会序列化为 JSON 对象,因为{'c', 'b', 'a'}
它不是有效的 json。如果您查看该x.__dict__
属性,您可以看到该对象将被表示为什么。
{'a': 1, 'b': 2, 'c': 3, '_fields': {'c', 'b', 'a'}}
如果您更改_fields
为列表,您还可以使用default
JSON.dumps 中的参数
这些是我为使您正在寻找的工作所做的更改
self._fields = ['a', 'b', 'c']
print(json.dumps(x, default=vars))
这是我的 canges 的完整代码。
from collections.abc import MutableMapping
import json
class TotallyADict(MutableMapping):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
self._fields = ['a', 'b', 'c']
def __getitem__(self, key):
if key in self._fields:
return getattr(self, key)
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def __setitem__(self, key, value):
if key in self._fields:
setattr(self, key, value)
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def __delitem__(self, key):
raise RuntimeError('Cannot delete fields from {}'.format(type(self).__name__))
def __iter__(self):
return iter(self._fields)
def __len__(self):
return len(self._fields)
def __contains__(self, k):
return k in self._fields
def copy(self):
return type(self)(**{k: getattr(self, k) for k in self._fields})
def __repr__(self):
return '{' + ', '.join('"{}": {}'.format(k, repr(getattr(self, k))) for k in self._fields) + '}'
def get(self, key, default=None):
if key in self._fields:
value = getattr(self, key)
if value is None:
value = default
return value
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def setdefault(self, key, default=None):
if key in self._fields:
value = getattr(self, key)
if value is None:
value = default
setattr(self, key, value)
return value
else:
raise KeyError('"{}" is not a field in {}'.format(key, type(self).__name__))
def pop(self, key, value=None):
raise RuntimeError('Cannot delete fields from {}'.format(type(self).__name__))
def keys(self):
return self._fields
def items(self):
return [(k, getattr(self, k)) for k in self._fields]
def values(self):
return [getattr(self, k) for k in self._fields]
def __eq__(self, other):
if type(self) is type(other):
for k in self._fields:
if getattr(self, k) != getattr(other, k):
return False
return True
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
x = TotallyADict(1, 2, 3)
print(json.dumps(x, default=vars))
您也可以尝试使用UserDict
https://docs.python.org/3/library/collections.html#collections.UserDict
推荐阅读
- c++ - 如何在 Windows 控制台应用程序中执行 popen()
- html - 如何删除css网格的空第二行
- java - 对象在该位置不存在。代码:-13010 HttpResult:404
- java - 如何在提供给 CompletableFuture 的 ExecutorService 上调用关闭?
- c# - 处理来自套接字流的数据时,集合被修改错误
- unity3d - Unity Firebase 云消息传递 SendAsync 缺失
- swiftui - .enumerated() 方法不适用于数组
- excel - 每次文本框值更改时,如何创建一个全局变量来重置值?
- c++ - 为什么我的 C++ 代码在执行前没有接受所有输入?它只需要输入并打印所有 cout 内容
- php - PHP preg_match 解析器 - 如何获取大写字母