如何使 Python 中的 json.dumps 忽略不可序列化字段

2024-03-02

我正在尝试使用 Construct2.9 库序列化解析一些二进制数据的输出。我想将结果序列化为 JSON。

packet是 Construct 类的实例Container.

显然它包含一个隐藏的_io类型的BytesIO- 查看输出dict(packet) below:

{
'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)显然会导致类型错误:

...

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 field?

我通过重写让代码可以工作JSONEncoder并返回None对于领域BytesIO类型,但这意味着我的序列化字符串包含大量"_io": null元素,我根本不想拥有......


带前导的按键_下划线并不是真正的“隐藏”,它们只是 JSON 的更多字符串。构造Container类只是一个具有排序的字典,_iokey 对于该类来说没有什么特别的。

您有两个选择:

  • 实施一个default仅返回替换值的钩子。
  • 过滤掉你知道不能工作的键值对before连载。

也许是第三个,但对 Construct 项目页面的随意扫描并不能告诉我它是否可用:有 Construct 输出 JSON 或至少一个 JSON 兼容的字典,也许通过使用适配器。

默认钩子无法阻止_io键被添加到输出中,但至少可以让您避免错误:

json.dumps(packet, default=lambda o: '<not serializable>')

过滤可以递归进行;这@functools.singledispatch()装饰者 https://docs.python.org/3/library/functools.html#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参数也可以显式跳过具有_开头的字符。这将有助于跳过构造库正在使用的所有其他“隐藏”属性。

Since Container is a dict子类,上面的代码会自动处理实例,例如packet.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使 Python 中的 json.dumps 忽略不可序列化字段 的相关文章

随机推荐