Saltar al contenido

Cómo hacer que json.dumps en Python ignore un campo no serializable

Este grupo especializado luego de ciertos días de trabajo y de recopilar de datos, obtuvieron la respuesta, esperamos que resulte de gran utilidad para tu trabajo.

Solución:

Llaves con protagonismo _ Los subrayados no están realmente ‘ocultos’, son solo más cadenas de JSON. El constructo Container clase es solo un diccionario con orden, el _io key no es nada especial para esa clase.

Tienes dos opciones:

  • implementar un default gancho que solo devuelve un valor de reemplazo.
  • Filtrar el keypares de valores que sabes que no pueden funcionar antes de serialización.

y quizás un tercero, pero un escaneo casual de las páginas del proyecto de Construct no me dice si está disponible: tiene JSON de salida de Construct o al menos un diccionario compatible con JSON, tal vez usando adaptadores.

El gancho predeterminado no puede evitar _io key se agregue a la salida, pero le permitiría al menos evitar el error:

json.dumps(packet, default=lambda o: '')

El filtrado se puede realizar de forma recursiva; el @functools.singledispatch() El decorador puede ayudar a mantener limpio dicho código:

from functools import singledispatch

_cant_serialize = object()

@singledispatch
def json_serializable(object, skip_underscore=False):
    """Filter a Python object to only include serializable object types

    In dictionaries, keys are converted to strings; if skip_underscore is true
    then keys starting with an underscore ("_") are skipped.

    """
    # default handler, called for anything without a specific
    # type registration.
    return _cant_serialize

@json_serializable.register(dict)
def _handle_dict(d, skip_underscore=False):
    converted = ((str(k), json_serializable(v, skip_underscore))
                 for k, v in d.items())
    if skip_underscore:
        converted = ((k, v) for k, v in converted if k[:1] != '_')
    return k: v for k, v in converted if v is not _cant_serialize

@json_serializable.register(list)
@json_serializable.register(tuple)
def _handle_sequence(seq, skip_underscore=False):
    converted = (json_serializable(v, skip_underscore) for v in seq)
    return [v for v in converted if v is not _cant_serialize]

@json_serializable.register(int)
@json_serializable.register(float)
@json_serializable.register(str)
@json_serializable.register(bool)  # redudant, supported as int subclass
@json_serializable.register(type(None))
def _handle_default_scalar_types(value, skip_underscore=False):
    return value

Tengo la implementación anterior un adicional skip_underscore argumento también, para omitir explícitamente keys que tienen un _ personaje al principio. Esto ayudaría a omitir todos los “ocultos” adicionales attributes la biblioteca Construct está usando.

Ya que Container es un dict subclase, el código anterior manejará automáticamente instancias como packet.

Ignorar un campo no serializable requiere una lógica adicional pesada, como se señaló correctamente en todas las respuestas anteriores.

Si realmente no necesita excluir el campo, puede generar un valor predeterminado en su lugar:

def safe_serialize(obj):
  default = lambda o: f"<>"
  return json.dumps(obj, default=default)

obj = "a": 1, "b": bytes() # bytes is non-serializable by default
print(safe_serialize(obj))

Eso producirá este resultado:

"a": 1, "b": "<>"

Este código imprimirá el nombre del tipo, lo que podría ser útil si desea implementar sus serializadores personalizados más adelante.

skipkeys no hace lo que piensas que hace, instruye al json.JSONEncoder saltear keys que no son de un básico tipo, no los valores del keys – es decir, si tuvieras un dictobject(): "foobar" se saltaría el object() key, mientras que sin skipkeys ajustado a True levantaría un TypeError.

Puedes sobrecargar JSONEncoder.iterencode() (y su parte más vulnerable) y realice un filtrado de anticipación allí, pero terminará reescribiendo prácticamente el json módulo, ralentizándolo en el proceso ya que no podrá beneficiarse de las partes compiladas. Lo que le sugiero es que procese previamente sus datos a través del filtrado iterativo y omita keys/ tipos que no desea en su JSON final. Entonces la json El módulo debería poder procesarlo sin instrucciones adicionales. Algo como:

import collections

class SkipFilter(object):

    def __init__(self, types=None, keys=None, allow_empty=False):
        self.types = tuple(types or [])
        self.keys = set(keys or [])
        self.allow_empty = allow_empty  # if True include empty filtered structures

    def filter(self, data):
        if isinstance(data, collections.Mapping):
            result =   # dict-like, use dict as a base
            for k, v in data.items():
                if k in self.keys or isinstance(v, self.types):  # skip key/type
                    continue
                try:
                    result[k] = self.filter(v)
                except ValueError:
                    pass
            if result or self.allow_empty:
                return result
        elif isinstance(data, collections.Sequence):
            result = []  # a sequence, use list as a base
            for v in data:
                if isinstance(v, self.types):  # skip type
                    continue
                try:
                    result.append(self.filter(v))
                except ValueError:
                    pass
            if result or self.allow_empty:
                return result
        else:  # we don't know how to traverse this structure...
            return data  # return it as-is, hope for the best...
        raise ValueError

Luego crea tu filtro:

import io

preprocessor = SkipFilter([io.BytesIO], ["_io"])  # double-whammy skip of io.BytesIO

En este caso, debería bastar con saltar solo por tipo, pero en caso de que el _io key contiene algunos otros datos indeseables, lo que garantiza que no estarán en el resultado final. De todos modos, puede filtrar los datos antes de pasarlos al JSONEncoder:

import json

json_data = json.dumps(preprocessor.filter(packet))  # no _io keys or io.BytesIO data...

Por supuesto, si su estructura contiene algunos otros datos exóticos o datos que se representan en JSON de manera diferente según su tipo, este enfoque podría estropearlo ya que convierte todas las asignaciones en dict y todas las secuencias en list. Sin embargo, para uso general esto debería ser más que suficiente.

Te invitamos a estimular nuestra labor mostrando un comentario o valorándolo te estamos eternamente agradecidos.

¡Haz clic para puntuar esta entrada!
(Votos: 2 Promedio: 4)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *