如何截断dict中的数据,以便生成的JSON不超过n个字节?

时间:2013-03-02 00:04:06

标签: python unicode json micro-optimization

我有一个python 2.7 dict,例如{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)

在我看来应该有一种优化方法!我真的一次剥离一个角色,感觉很糟糕。

我的问题非常接近this one,除了我使用python 2.7,并且只要源字符串包含非ASCII unicode字符,json编码器就会生成相当长的JSON字符串。

另外我很确定这会打破UTF-16代理对......

3 个答案:

答案 0 :(得分: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 = (',', ':'))

我不确定这实际上在您的用例中加快速度。对于非常小的物体和最大尺寸,如果它实际上减慢了速度,我不会感到惊讶。但对于超过最大尺寸的巨大物体,它可能会有很大帮助。

答案 1 :(得分:0)

如何计算每个条目的序列化大小

然后选择尽可能多的元素,使其具有所需的长度?

无论哪种方式,这听起来总体上是一个非常糟糕的主意。

答案 2 :(得分:0)

为什么不在post you linked中使用该策略: 你测量第一个生成的json,然后从首选顺序中删除正确数量的字符值。

否则你可以通过计算猜测json使用的字符数:对于每个映射变量,这些字符"":"",加上整体{},减去逗号。 (显然,除非你没有更复杂的嵌套列表)

只要你使用u''表示法(不确定,但不应该很难检查),unicode功能就不成问题了