Estoy trabajando en una API que devuelve JSON. Estoy registrando mis respuestas, y a veces el JSON es absurdamente largo y básicamente obstruye mis archivos de registro. ¿Hay alguna forma ordenada de reducir la longitud de un JSON, solo para registrar visualmente los datos? (no vigente en producción)

El enfoque básico es reducir matrices en una longitud de 5 a [primeros 2, "...", últimos 2] y diccionarios con más de 4 elementos a { primeros 4, "...": "..."}

El siguiente código es feo. Soy consciente de que debería ser una solución recursiva que reduzca los elementos de la misma manera para un JSON de profundidad arbitraria; actualmente solo lo hace para la profundidad 2.

def log_reducer(response_log):
original_response_log = response_log
try:
    if type(response_log) == dict:
        if len(response_log) >= 4:  # {123456}
            response_log = dict(list(response_log.items())[:4])
            response_log.update({"...": "..."})  # {1234...}
        for key, value in response_log.items():
            if type(value) == list:
                if len(value) >= 5:  # {key:[123456]}
                    new_item = value[:2] + ['...'] + value[-2:]  # {[12...56]}
                    response_log.update({key: new_item})
            if type(value) == dict:
                if len(value) >= 4:  # {key:{123456}}
                    reduced_dict = dict(list(value.items())[:4])
                    reduced_dict.update({"...": "..."})
                    response_log.update({key: reduced_dict})  # {{1234...}}

    elif type(response_log) == list:
        if len(response_log) >= 5:  # [123456]
            response_log = response_log[:2] + ['...'] + response_log[-2:]  # [12...56]
        for inner_item in response_log:
            if type(inner_item) == list:
                if len(inner_item) >= 5:  # [[123456]]
                    reduced_list = inner_item[:2] + ['...'] + inner_item[-2:]  # [[12...56]]
                    response_log.remove(inner_item)
                    response_log.append(reduced_list)
            if type(inner_item) == dict:
                if len(inner_item) >= 4:  # [{123456}]
                    reduced_dict = dict(list(inner_item.items())[:4])
                    reduced_dict.update({"...": "..."})  # [{1234...}]
                    response_log.remove(inner_item)
                    response_log.append(reduced_dict)
except Exception as e:
    return original_response_log
return response_log

El registro de respuestas devuelto se registra con logger.info (cadena (registro de respuestas))

Como puede ver, el hecho de que puede haber matrices o diccionarios en todos los niveles hace que esta tarea sea un poco más compleja, y estoy luchando por encontrar una biblioteca o código recortado de cualquier tipo que lo simplifique. Si alguien quiere darle una oportunidad, lo agradecería mucho.

Puedes usar un JSON de prueba como este para verlo en efecto:

test_json = {"works": [1, 2, 3, 4, 5, 6],
             "not_affected": [{"1": "1", "2": "2", "3": "3", "4": "4", "5": "5"}],
             "1": "1", "2": "2", "3": "3",
             "removed": "removed"
             }
print("original", test_json)
reduced_log = log_reducer(test_json)
print("reduced", reduced_log)

print("original", test_json)
reduced_log = log_reducer([test_json])  # <- increases nesting depth
print("reduced", reduced_log)
0
c8999c 3f964f64 19 feb. 2020 a las 12:24

2 respuestas

La mejor respuesta

Esta respuesta utiliza la idea de @ calceamenta, pero implementa la lógica de corte real:

def recursive_reduce(obj):
    if isinstance(obj, (float, str, int, bool, type(None))):
        return obj

    if isinstance(obj, dict):
        keys = list(sorted(obj))
        obj['...'] = '...'

        if len(keys) > 5:
            new_keys = keys[:2] + ["..."] + keys[-2:]
        else:
            new_keys = keys

        new_dict = {x:obj[x] for x in new_keys}
        for k, v in new_dict.items():
            new_dict[k] = recursive_reduce(v)

        return new_dict

    if isinstance(obj, list):
        if len(obj) > 5:
            new_list = obj[:2] + ["..."] + obj[-2:]
        else:
            new_list = obj

        for i, v in enumerate(new_list):
            new_list[i] = recursive_reduce(v)

        return new_list

    return str(obj)

test_json = {"works": [1, 2, 3, 4, 5, 6],
             "not_affected": [{"1": "1", "2": "2", "3": "3", "4": "4", "5": "5"}],
             "1": "1", "2": "2", "3": "3",
             "removed": "removed"
             }

print("original", test_json)
reduced_log = recursive_reduce(test_json)
print("reduced", reduced_log)

Salida:

original {'works': [1, 2, 3, 4, 5, 6], 'not_affected': [{'1': '1', '2': '2', '3': '3', '4': '4', '5': '5'}], '1': '1', '2': '2', '3': '3', 'removed': 'removed'}
reduced {'1': '1', '2': '2', '...': '...', 'removed': 'removed', 'works': [1, 2, '...', 5, 6]}

Espero que esto :) ayude

1
Ed Ward 19 feb. 2020 a las 10:35

Puede sobrescribir la representación de cadena de dictados y listas en Python utilizando el método def __str __ (): . Usando esto simplemente llame recursivamente a la función de impresión en todos los elementos. Puede tener una plantilla simple como esta:

def custom_print(obj):
    log_str = ''
    if type(obj) == list:
        for item in obj:
            log_str += custom_print(item)
    elif type(obj) == dict:
        for k, item in obj.items():
            custom_print(item)

Utilice esta función de registro personalizada para imprimir en su archivo de registro según el formato de su archivo de registro.

1
calceamenta 19 feb. 2020 a las 09:41