如何截断字典中的数据,使结果JSON不超过n字节?
我有一个Python 2.7的字典,比如 {u"eat": u"糖果", u"drink": u"café"}
,我需要把它转换成JSON格式。这个JSON字符串必须是普通的ASCII格式,并且长度要少于256个字符。
到目前为止,我写了以下代码:
import json
def payload_to_json(payload, max_size = 256):
while True:
json_string = json.dumps(payload, separators = (',', ':'))
if len(json_string) <= max_size:
return json_string
max_length, found_key = 0, None
for key, value in payload.iteritems():
length = len(value)
if length > max_length:
max_length = length
found_key = key
if max_length == 0:
return "" # just in case max_size is really low
payload[found_key] = payload[found_key][:-1] # remove one char
它的运行效果如我所预期的那样:
>>> payload = {u"eat": u"糖果", u"drink": u"café"}
>>> print payload_to_json(payload)
{"drink":"caf\u00e9","eat":"\u7cd6\u679c"}
>>> print payload_to_json(payload, max_size=41)
{"drink":"caf","eat":"\u7cd6\u679c"}
>>> print payload_to_json(payload, max_size=35)
{"drink":"ca","eat":"\u7cd6\u679c"}
>>> print payload_to_json(payload, max_size=34)
{"drink":"c","eat":"\u7cd6\u679c"}
>>> print payload_to_json(payload, max_size=30)
{"drink":"c","eat":"\u7cd6"}
>>> print payload_to_json(payload, max_size=21)
{"drink":"","eat":""}
>>> print payload_to_json(payload, max_size=20)
我觉得应该有办法让这个过程更优化!我现在是一个一个字符地去掉,感觉这样做很不对劲。
我的问题和这个问题很相似,只不过我用的是Python 2.7,而JSON编码器在源字符串包含非ASCII的Unicode字符时,生成的JSON字符串会特别长。
而且我很确定这样做在处理UTF-16的代理对时会出问题……
3 个回答
我们可以先计算一下每个条目的序列化大小。
然后再选择尽可能多的元素,直到达到我们想要的长度。
不过,不管怎么说,这听起来整体上都是个很糟糕的主意。
你为什么不试试你链接的那篇帖子里的方法呢?在那篇帖子里,你可以先测量一下生成的 JSON 数据的长度,然后再按照你想要的顺序,从值中去掉合适数量的字符。
另外,你也可以通过计算字符的数量来估算 JSON 数据的长度:对于每个映射的变量,都会有这些字符 "":"",
,再加上整体的 {}
,然后减去一个逗号。(当然,如果你没有更复杂的嵌套列表的话,这样计算是有效的)
关于 Unicode 的功能,只要你使用 u''
的写法,应该不会有问题。(我不太确定,但检查起来应该不难)
如果你想让这个过程更快(不过,除非你确定这是程序中一个性能瓶颈,否则不建议这样做),你可以先估算一下需要去掉多少个字符,然后再处理剩下的部分。
首先,如果你需要去掉52个字符,而你有10个键,那么你需要从2个键中各去掉6个字符,从其他8个键中各去掉5个字符,对吧?当然,问题是你可能会试图从一个只有4个字符的地方去掉6个字符,这样就会超出限制2个字符。不过,你可以记录下这些剩余的字符,等到最后再处理它们。剩余的字符不太可能多到需要再用“快速”版本处理一次,所以你不如直接使用“慢速”版本。
def payload_to_json(payload, max_size = 256):
json_string = json.dumps(payload, separators = (',', ':'))
chars_to_strip = len(json_string) - max_size
if chars_to_strip <= 0:
return json_string
key_count = len(payload)
chars_per_key, extras = divmod(chars_to_strip, key_count)
leftover = 0
for i, key in enumerate(payload):
to_strip = chars_per_key + (i < extras)
orig_len = len(payload[key])
if orig_len < to_strip:
payload[key] = ''
leftover += to_strip - orig_len
else:
payload[key] = payload[key][:-to_strip]
if leftover:
return slow_payload_to_json(payload, max_size)
else:
return json.dumps(payload, separators = (',', ':'))
我不太确定这样做是否真的能在你的使用场景中加快速度。对于非常小的对象和最大尺寸,我不会感到惊讶如果这样反而让速度变慢。但是对于那些远超最大尺寸的大对象,这样做可能会有很大帮助。