如何截断字典中的数据,使结果JSON不超过n字节?

2 投票
3 回答
3739 浏览
提问于 2025-04-17 17:39

我有一个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 个回答

0

我们可以先计算一下每个条目的序列化大小

然后再选择尽可能多的元素,直到达到我们想要的长度。

不过,不管怎么说,这听起来整体上都是个很糟糕的主意。

0

你为什么不试试你链接的那篇帖子里的方法呢?在那篇帖子里,你可以先测量一下生成的 JSON 数据的长度,然后再按照你想要的顺序,从值中去掉合适数量的字符。

另外,你也可以通过计算字符的数量来估算 JSON 数据的长度:对于每个映射的变量,都会有这些字符 "":"",,再加上整体的 {},然后减去一个逗号。(当然,如果你没有更复杂的嵌套列表的话,这样计算是有效的)

关于 Unicode 的功能,只要你使用 u'' 的写法,应该不会有问题。(我不太确定,但检查起来应该不难)

1

如果你想让这个过程更快(不过,除非你确定这是程序中一个性能瓶颈,否则不建议这样做),你可以先估算一下需要去掉多少个字符,然后再处理剩下的部分。

首先,如果你需要去掉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 = (',', ':'))

我不太确定这样做是否真的能在你的使用场景中加快速度。对于非常小的对象和最大尺寸,我不会感到惊讶如果这样反而让速度变慢。但是对于那些远超最大尺寸的大对象,这样做可能会有很大帮助。

撰写回答