Отключить научную нотацию в выводе python json.dumps

json.dumps выводит небольшие значения с плавающей запятой или десятичными значениями, используя научную нотацию, что неприемлемо для приложения json-rpc, на который этот вывод отправляется.

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

Вместо этого я хочу этот вывод:

'{"x": 0.0000001}'

Было бы идеально избегать введения дополнительных зависимостей.

Ответ 1

Один способ форматирования

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.

Как бы я это сделал, даже если это хромовый метод.

Ответ 2

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. Он отлично работает в моем проекте! Надеюсь, что это может помочь кому-то !!

Ответ 3

найден где-то в Интернете

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]}'