如何使Python中的json.dump忽略不可序列化的字段
我试图用Construct2.9库序列化解析一些二进制数据的输出。我想将结果序列化为JSON如何使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
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的更多字符串。ConstructContainer
类只是一个带有顺序的字典,而\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>>"}