python - 如何使 Python 中的 json.dumps 忽略不可序列化的字段
问题描述
我正在尝试使用 Construct2.9 库对解析一些二进制数据的输出进行序列化。我想将结果序列化为 JSON。
packet
是 Construct 类的一个实例Container
。
显然它包含一个隐藏_io
类型BytesIO
- 见dict(packet)
下面的输出:
{
'packet_length': 76, 'uart_sent_time': 1, 'frame_number': 42958,
'subframe_number': 0, 'checksum': 33157, '_io': <_io.BytesIO object at 0x7f81c3153728>,
'platform':661058, 'sync': 506660481457717506, 'frame_margin': 20642,
'num_tlvs': 1, 'track_process_time': 593, 'chirp_margin': 78,
'timestamp': 2586231182, 'version': 16908293
}
现在,调用json.dumps(packet)
显然会导致 TypeError:
...
File "/usr/lib/python3.5/json/__init__.py", line 237, in dumps
**kw).encode(obj)
File "/usr/lib/python3.5/json/encoder.py", line 198, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python3.5/json/encoder.py", line 256, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python3.5/json/encoder.py", line 179, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <_io.BytesIO object at 0x7f81c3153728> is not JSON serializable
但是,我感到困惑的是,运行json.dumps(packet, skipkeys=True)
会导致完全相同的错误,而我希望它会跳过该_io
字段。这里有什么问题?为什么skipkeys
不允许我跳过该_io
字段?
我通过覆盖JSONEncoder
和返回类型None
字段来使代码工作BytesIO
,但这意味着我的序列化字符串包含大量"_io": null
元素,我根本不想拥有这些元素......
解决方案
带有前导_
下划线的键并不是真正“隐藏”的,它们只是 JSON 的更多字符串。ConstructContainer
类只是一个带有排序的字典,_io
键对于该类没有什么特别的。
你有两个选择:
- 实现一个
default
只返回替换值的钩子。 - 在序列化之前过滤掉你知道不能工作的键值对。
也许还有第三个,但是随便浏览一下 Construct 项目页面并不能告诉我它是否可用:让 Construct 输出 JSON 或至少一个与 JSON 兼容的字典,可能是通过使用适配器。
默认钩子不能阻止将_io
密钥添加到输出中,但至少可以让您避免错误:
json.dumps(packet, default=lambda o: '<not serializable>')
过滤可以递归进行;@functools.singledispatch()
装饰器可以帮助保持这样的代码干净:
from functools import singledispatch
_cant_serialize = object()
@singledispatch
def json_serializable(object, skip_underscore=False):
"""Filter a Python object to only include serializable object types
In dictionaries, keys are converted to strings; if skip_underscore is true
then keys starting with an underscore ("_") are skipped.
"""
# default handler, called for anything without a specific
# type registration.
return _cant_serialize
@json_serializable.register(dict)
def _handle_dict(d, skip_underscore=False):
converted = ((str(k), json_serializable(v, skip_underscore))
for k, v in d.items())
if skip_underscore:
converted = ((k, v) for k, v in converted if k[:1] != '_')
return {k: v for k, v in converted if v is not _cant_serialize}
@json_serializable.register(list)
@json_serializable.register(tuple)
def _handle_sequence(seq, skip_underscore=False):
converted = (json_serializable(v, skip_underscore) for v in seq)
return [v for v in converted if v is not _cant_serialize]
@json_serializable.register(int)
@json_serializable.register(float)
@json_serializable.register(str)
@json_serializable.register(bool) # redudant, supported as int subclass
@json_serializable.register(type(None))
def _handle_default_scalar_types(value, skip_underscore=False):
return value
我在上面的实现中也有一个额外的参数,可以显式跳过开头skip_underscore
有字符的键。_
这将有助于跳过 Construct 库正在使用的所有其他“隐藏”属性。
由于Container
是dict
子类,上面的代码会自动处理packet
.
推荐阅读
- javascript - 根据特定数据属性的值差异对内容进行排序和追加
- python - 如何修复 TypeError:Pandas 中的日期时间对象无法理解数据类型
- mongodb - MongoDB Aggregator - 将在 30 秒内发生的唯一项目分组
- javascript - Node.js TypeError:JSON.parseInt 不是函数
- reactjs - 无法更改导航栏的颜色
- database - 如何在颤动中从 SQLITE 表中获取不同的记录?
- docker - Cyberark Conjur 服务器的设置
- c++ - 如何将数组中的元素向右移动?
- python - 如何在 Python 中使用 Docx 打开 PDF 文件?
- java - 难以使用 Selenium 和 Java 处理新打开的选项卡