在python json.dumps输出中禁用科学记数法

时间:2013-09-21 19:12:49

标签: python json scientific-notation

json.dumps使用科学记数法输出小的浮点数或小数值,这对于发送此输出的json-rpc应用程序是不可接受的。

>>> import json
>>> json.dumps({"x": 0.0000001})
'{"x": 1e-07}'

我想要这个输出:

'{"x": 0.0000001}'

避免引入其他依赖项是理想的。

4 个答案:

答案 0 :(得分:3)

格式化的一种方法

evil = {"x": 0.00000000001}

是窃取Decimal的“f”格式化程序。这是我发现的唯一一种避免裁剪问题和指数的简单方法,但它不是空间效率

class FancyFloat(float):
    def __repr__(self):
        return format(Decimal(self), "f")

要使用它,您可以制作一个“十进制”输入

的编码器
class JsonRpcEncoder(json.JSONEncoder):
    def decimalize(self, val):
        if isinstance(val, dict):
            return {k:self.decimalize(v) for k,v in val.items()}

        if isinstance(val, (list, tuple)):
            return type(val)(self.decimalize(v) for v in val)

        if isinstance(val, float):
            return FancyFloat(val)

        return val

    def encode(self, val):
        return super().encode(self.decimalize(val))

JsonRpcEncoder().encode(evil)
#>>> '{"x": 0.00000000000999999999999999939496969281939810930172340963650867706746794283390045166015625}'

或者,当然,您可以将十进制移到函数中并在json.dumps之前调用它。

即使这是一种蹩脚的方法,我也会这样做。

答案 1 :(得分:0)

def pretty_float_json_dumps(json_obj):
dumps_str = ""

if isinstance(json_obj, dict): 
    dumps_str += "{"
    for k,v in json_obj.items():
        dumps_str += json.dumps(k)+":"
        if isinstance(v, float): 
            float_tmp_str = ("%.16f" % v).rstrip("0")
            dumps_str += (float_tmp_str+'0' if float_tmp_str.endswith('.') else float_tmp_str) + ','
        elif isinstance(v, list) or isinstance(v, dict): 
            dumps_str += self_json_dumps(v)+','
        else:
            dumps_str += self_json_dumps(v)+','
    if dumps_str.endswith(','):
        dumps_str = dumps_str[:-1]
    dumps_str += "}"
elif isinstance(json_obj, list): 
    dumps_str += "["
    for v in json_obj:
        if isinstance(v, float): 
            float_tmp_str = ("%.16f" % v).rstrip("0")
            dumps_str += (float_tmp_str+'0' if float_tmp_str.endswith('.') else float_tmp_str) + ','
        elif isinstance(v, list) or isinstance(v, dict): 
            dumps_str += self_json_dumps(v)+','
        else:
            dumps_str += self_json_dumps(v)+','
    if dumps_str.endswith(','):
        dumps_str = dumps_str[:-1]
    dumps_str += "]"
else:
    dumps_str += json.dumps(json_obj)
return dumps_str

我找不到避免将0.00001转换为1e-5的问题的答案,所以我写了一个pretty_float_json_dumps函数。在我的项目中效果很好!希望它可以帮助某人!

答案 2 :(得分:0)

这是补充评论,不是避免在 json.dumps 中使用科学记数法的完整答案。通过额外的一轮解析,parse_float 上的 json.loads 选项可以帮助处理一些事情,例如

$ python
Python 3.7.10 | packaged by conda-forge | (default, Feb 19 2021, 16:07:37) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> data = {"x": 0.0000001}
>>> json.dumps(data)
'{"x": 1e-07}'
>>> data = json.loads(json.dumps(data), parse_float=lambda x: round(float(x), 6))
>>> json.dumps(data)
'{"x": 0.0}'

这只会避免四舍五入为零的小值的科学记数法。虽然这对于大型数据集效率不高,但它在某些用例中会有所帮助。这并不能完全避免科学记数法。

>>> data = {"x": 0.00001}
>>> data = json.loads(json.dumps(data), parse_float=lambda x: round(float(x), 6))
>>> json.dumps(data)
'{"x": 1e-05}'

答案 3 :(得分:-1)

在互联网上找到了某个地方

import json
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.2f')

之后

>>> json.dumps({'gps': [51.5, 17.5]})
'{"gps": [51.5, 17.5]}'