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

如何使Python中的json.dump忽略不可序列化的字段,python,json,python-3.x,construct,Python,Json,Python 3.x,Construct,我试图用Construct2.9库序列化解析一些二进制数据的输出。我想将结果序列化为JSON packet是构造类容器的实例 显然,它包含一个类型为BytesIO的隐藏\u io——请参见下面的dict(packet)输出: { 'packet_length': 76, 'uart_sent_time': 1, 'frame_number': 42958, 'subframe_number': 0, 'checksum': 33157, '_io': <_io.BytesIO objec

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

packet
是构造类
容器的实例

显然,它包含一个类型为
BytesIO
的隐藏
\u io
——请参见下面的
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,skipkeys=True)
会导致完全相同的错误,而我希望它会跳过
\u io
字段。这里有什么问题?为什么
skipkeys
不允许我跳过
\u io
字段


我通过覆盖
jsonecoder
并为
BytesIO
类型的字段返回
None
,使代码正常工作,但这意味着我的序列化字符串包含大量
“\u io”:null
元素,我不希望有这些元素…

带有前导
\ucode>下划线的键实际上不是“隐藏”的,它们只是JSON的更多字符串。Construct
Container
类只是一个带有顺序的字典,而
\u io
键对该类来说并不是什么特殊的东西

您有两个选择:

  • 实现一个只返回替换值的
    default
    hook
  • 筛选出在序列化之前无法工作的键值对
也许还有第三个,但对构建项目页面的随意扫描并不能告诉我它是否可用:使用构建输出JSON或至少一个JSON兼容的字典,可能是通过使用适配器

默认挂钩无法阻止将
\u io
键添加到输出中,但至少可以避免错误:

json.dumps(packet, default=lambda o: '<not serializable>')
我在上面的实现中还有一个额外的
skip_下划线
参数,用于显式跳过开头有
字符的键。这将有助于跳过构造库正在使用的所有其他“隐藏”属性

由于
Container
是一个
dict
子类,上述代码将自动处理诸如
packet

这样的实例,它不会做您可能认为它做的事情-它指示跳过非基本类型的键,而不是键的值-例如,如果您有
dict
{object():“foobar”}
它将跳过
object()
键,而如果
skipkeys
未设置为
True
则会引发
TypeError

您可以重载(及其底层)并在那里执行前瞻性过滤,但最终您将重写
json
模块,在过程中减慢它的速度,因为您将无法从编译的部分中获益。我建议您通过迭代过滤预处理数据,并跳过最终JSON中不需要的键/类型。然后,
json
模块应该能够在没有任何额外指令的情况下处理它。比如:

import collections

class SkipFilter(object):

    def __init__(self, types=None, keys=None, allow_empty=False):
        self.types = tuple(types or [])
        self.keys = set(keys or [])
        self.allow_empty = allow_empty  # if True include empty filtered structures

    def filter(self, data):
        if isinstance(data, collections.Mapping):
            result = {}  # dict-like, use dict as a base
            for k, v in data.items():
                if k in self.keys or isinstance(v, self.types):  # skip key/type
                    continue
                try:
                    result[k] = self.filter(v)
                except ValueError:
                    pass
            if result or self.allow_empty:
                return result
        elif isinstance(data, collections.Sequence):
            result = []  # a sequence, use list as a base
            for v in data:
                if isinstance(v, self.types):  # skip type
                    continue
                try:
                    result.append(self.filter(v))
                except ValueError:
                    pass
            if result or self.allow_empty:
                return result
        else:  # we don't know how to traverse this structure...
            return data  # return it as-is, hope for the best...
        raise ValueError
然后创建过滤器:

import io

preprocessor = SkipFilter([io.BytesIO], ["_io"])  # double-whammy skip of io.BytesIO
在这种情况下,仅按类型跳过就足够了,但如果
\u io
键包含一些其他不需要的数据,这将保证它不会出现在最终结果中。无论如何,在将数据传递给
jsonecoder
之前,您只需过滤数据即可:

import json

json_data = json.dumps(preprocessor.filter(packet))  # no _io keys or io.BytesIO data...

当然,如果您的结构包含一些其他外来数据或根据其类型以不同方式在JSON中表示的数据,这种方法可能会把它搞砸,因为它会将所有映射转换为
dict
,将所有序列转换为
list
。但是,对于一般用途来说,这应该足够了。

忽略不可序列化的字段需要大量额外的逻辑,正如前面所有答案中正确指出的那样

如果确实不需要排除该字段,则可以生成默认值:

def-safe_序列化(obj):
默认值=λo:f“”
返回json.dumps(obj,default=default)
obj={“a”:1,“b”:bytes()}#默认情况下,字节是不可序列化的
打印(安全序列化(obj))
这将产生这样的结果:

{"a": 1, "b": "<<non-serializable: bytes>>"}
{“a”:1,“b”:“”}

此代码将打印类型名称,如果您以后要实现自定义序列化程序,这可能会很有用。

skipkeys
只忽略非基本键,而不忽略values@EdwardMinnix啊,我知道我错过了什么。。。那么,有没有办法完全跳过某个字段的编码?@mz8i您找到了为什么Construct2.9中的更改会产生
\u io
?在2.9更新之前,我已经使用Construct多年了,现在已经很好地转换为json。@MartijnPieters这似乎对嵌套对象不起作用-有没有一种很好的通用方法来实现这一点并使它对任何对象都起作用?e、 我有一个Author对象,它有一个Publications属性,它是一个publication对象数组。。。有人吗?@MarkZhukovsky:这确实支持嵌套对象,因为每个注册函数都递归。因此处理列表中的字典是因为函数handlinglists对列表中的每个元素调用
json_serializable()
。您只需为其他类型注册附加函数,例如您的作者类型。确保为类型的每个属性调用
json\u serializable()
。问题中的对象是
dict
的子类,这就是为什么我们不必为它注册任何特殊的东西,但显然您的
作者类型不是。@MartijnPieters感谢您的快速响应。我确实可以通过添加作者和出版物来实现它,而我对嵌套注释的理解是错误的。但我所做的只是复制handle_dict的函数,并将d.items()更改为d.u dict_uu.items(),它成功了。是否有一种方法可以通用地处理任何类类型,这样就不需要手动添加特定的类了?我来自不同的语言,所以谢谢你的耐心。@MarkZhukovsky:c
import json

json_data = json.dumps(preprocessor.filter(packet))  # no _io keys or io.BytesIO data...
{"a": 1, "b": "<<non-serializable: bytes>>"}