Mostrar un mensaje de error mejor que "No se pudo decodificar ningún objeto JSON"

128

Código de Python para cargar datos de algún archivo JSON largo y complicado:

with open(filename, "r") as f:
  data = json.loads(f.read())

(nota: la mejor versión del código debe ser:

with open(filename, "r") as f:
  data = json.load(f)

pero ambos exhiben un comportamiento similar)

Para muchos tipos de error JSON (delimitadores faltantes, barras invertidas incorrectas en cadenas, etc.), esto imprime un mensaje útil que contiene la línea y el número de columna donde se encontró el error JSON.

Sin embargo, para otros tipos de error JSON (incluido el clásico "usar una coma en el último elemento de una lista", pero también otras cosas como capitalizar verdadero / falso), la salida de Python es solo:

Traceback (most recent call last):
  File "myfile.py", line 8, in myfunction
    config = json.loads(f.read())
  File "c:\python27\lib\json\__init__.py", line 326, in loads
    return _default_decoder.decode(s)
  File "c:\python27\lib\json\decoder.py", line 360, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "c:\python27\lib\json\decoder.py", line 378, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

Para ese tipo de ValueError, ¿cómo hace que Python le diga dónde está el error en el archivo JSON?

OJW
fuente
¿Podrías volcar un extracto de tu archivo?
Ketouem
No estoy tratando de encontrar el error en un archivo en particular ahora; Estoy tratando de modificar mi programa para que resalte el error en cualquier archivo futuro que lea.
OJW
2
No está directamente relacionado, pero podría hacerlo en json.load(f)lugar dejson.loads(f.read())
Martin Samson
@OJW, ¿en qué versión de Python se comportó este comportamiento?
jxramos
Python 3.8.1 ahora da la posición de error "Valor esperado: línea 1 columna 21 (char 20)"
OJW

Respuestas:

173

Descubrí que el simplejsonmódulo proporciona errores más descriptivos en muchos casos en los que el módulo incorporado jsones vago. Por ejemplo, para el caso de tener una coma después del último elemento de una lista:

json.loads('[1,2,]')
....
ValueError: No JSON object could be decoded

lo cual no es muy descriptivo. La misma operación con simplejson:

simplejson.loads('[1,2,]')
...
simplejson.decoder.JSONDecodeError: Expecting object: line 1 column 5 (char 5)

¡Mucho mejor! Del mismo modo para otros errores comunes como capitalizar True.

tom
fuente
18
Las versiones futuras de Python incluirán esas mejoras; Es el mismo proyecto debajo.
Martijn Pieters
1
@ user2016290 Editar archivos de núcleo / paquete directamente es una mala idea. Python es un parche fácil de usar, así que es mejor hacerlo en código.
Rebs
2
@jxramos: El OP usó Python 2.7, como se desprende del rastreo. Una prueba rápida en ideone.com (Python 3.7.3) muestra que la jsonbiblioteca stdlib se ha actualizado y proporciona el nuevo formato de mensaje de error. Sin embargo, no tengo tiempo para rastrear versiones exactas en este momento.
Martijn Pieters
1
@jxramos lo encontró, Python 3.5 actualizó las excepciones: bugs.python.org/issue19361 (a través de docs.python.org/3/whatsnew/3.5.html#improved-modules ).
Martijn Pieters
15

No podrá hacer que Python le diga dónde está incorrecto el JSON. Tendrá que usar un linter en línea en algún lugar como este

Esto le mostrará un error en el JSON que está tratando de decodificar.

myusuf3
fuente
2
¿Existen herramientas sin conexión que pueden hacer esto para archivos JSON confidenciales?
OJW
@OJW no lo sé, pero eso debería resolver el problema que tienes o al menos dejarte arreglar tu json roto.
myusuf3
12
Mi archivo JSON está bien: estoy tratando de hacer que mi programa imprima mensajes de error útiles que sean comprensibles para cualquiera. Decirles "deshacerse de esa coma en la línea 13 columna 32" es bueno. Decirles "hay un error en alguna parte de su archivo, por favor cárguelo en Internet donde la gente lo ayudará" es malo.
OJW
7

Puede probar la biblioteca rson que se encuentra aquí: http://code.google.com/p/rson/ . También estoy en PYPI: https://pypi.python.org/pypi/rson/0.9 para que pueda usar easy_install o pip para obtenerlo.

para el ejemplo dado por tom:

>>> rson.loads('[1,2,]')
...
rson.base.tokenizer.RSONDecodeError: Unexpected trailing comma: line 1, column 6, text ']'

RSON está diseñado para ser un superconjunto de JSON, por lo que puede analizar archivos JSON. También tiene una sintaxis alternativa que es mucho más agradable para que los humanos la vean y editen. Lo uso bastante para archivos de entrada.

En cuanto a la capitalización de valores booleanos: parece que rson lee booleanos con mayúsculas incorrectamente como cadenas.

>>> rson.loads('[true,False]')
[True, u'False']
Brad Campbell
fuente
4

Tuve un problema similar y se debió a comillas simples. El estándar JSON ( http://json.org ) solo habla sobre el uso de comillas dobles, por lo que debe ser que la jsonbiblioteca de Python solo admite comillas dobles.

Caballero samar
fuente
3

Para mi versión particular de este problema, seguí y busqué la declaración de función load_json_file(path)dentro del packaging.pyarchivo, luego pasé una printlínea de contrabando :

def load_json_file(path):
    data = open(path, 'r').read()
    print data
    try:
        return Bunch(json.loads(data))
    except ValueError, e:
        raise MalformedJsonFileError('%s when reading "%s"' % (str(e),
                                                               path))

De esa manera, imprimiría el contenido del archivo json antes de ingresar el try-catch, y de esa manera, incluso con mi conocimiento de Python apenas existente, pude averiguar rápidamente por qué mi configuración no podía leer el archivo json.
(Fue porque había configurado mi editor de texto para escribir una lista de materiales UTF-8 ... estúpido)

Solo menciono esto porque, aunque tal vez no sea una buena respuesta al problema específico del OP, este fue un método bastante rápido para determinar la fuente de un error muy opresivo. Y apuesto a que muchas personas se toparán con este artículo que están buscando una solución más detallada para a MalformedJsonFileError: No JSON object could be decoded when reading …. Entonces eso podría ayudarlos.

WoodrowShigeru
fuente
Debe usar el administrador de contexto para el archivo I / O ( with open(fn) as f), se encarga de cerrar el archivo en una excepción para usted. en.wikibooks.org/wiki/Python_Programming/...
REBS
1
+1. Si pudieras mostrar un ejemplo de un parche en el comportamiento estándar, sería genial
Craig Brett
Lo siento, nunca toqué ningún código de Python después de que se resolvió ese problema. Tal vez alguien más puede ayudar?
WoodrowShigeru
3

En cuanto a mí, mi archivo json es muy grande, cuando uso común jsonen python obtiene el error anterior.

Después de instalar simplejsonpor sudo pip install simplejson.

Y luego lo resolví.

import json
import simplejson


def test_parse_json():
    f_path = '/home/hello/_data.json'
    with open(f_path) as f:
        # j_data = json.load(f)      # ValueError: No JSON object could be decoded
        j_data = simplejson.load(f)  # right
    lst_img = j_data['images']['image']
    print lst_img[0]


if __name__ == '__main__':
    test_parse_json()
Jayhello
fuente
1

Tuve un problema similar, este era mi código:

    json_file=json.dumps(pyJson)
    file = open("list.json",'w')
    file.write(json_file)  

    json_file = open("list.json","r")
    json_decoded = json.load(json_file)
    print json_decoded

El problema era que me había olvidado de file.close() hacerlo y lo solucioné.

Habib Kazemi
fuente
También funcionó para mí, no sé por qué no tuve este problema antes.
pceccon
Debe usar el administrador de contexto para el archivo I / O ( with open(fn) as f), se encarga de cerrar el archivo en una excepción para usted. en.wikibooks.org/wiki/Python_Programming/...
REBS
0

La respuesta aceptada es la más fácil de solucionar. Pero en caso de que no pueda instalar el simplejson debido a la política de su empresa, le propongo la siguiente solución para solucionar el problema particular de "usar comas en el último elemento de una lista" :

  1. Cree una clase secundaria "JSONLintCheck" para heredar de la clase "JSONDecoder" y anule el método init de la clase "JSONDecoder" como se muestra a continuación:

    def __init__(self, encoding=None, object_hook=None, parse_float=None,parse_int=None, parse_constant=None, strict=True,object_pairs_hook=None)        
            super(JSONLintCheck,self).__init__(encoding=None, object_hook=None,      parse_float=None,parse_int=None, parse_constant=None, strict=True,object_pairs_hook=None)
            self.scan_once = make_scanner(self)
  1. make_scanner es una nueva función que solía anular el método 'scan_once' de la clase anterior. Y aquí hay un código para ello:
  1 #!/usr/bin/env python
  2 from json import JSONDecoder
  3 from json import decoder
  4 import re
  5
  6 NUMBER_RE = re.compile(
  7     r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
  8     (re.VERBOSE | re.MULTILINE | re.DOTALL))
  9
 10 def py_make_scanner(context):
 11     parse_object = context.parse_object
 12     parse_array = context.parse_array
 13     parse_string = context.parse_string
 14     match_number = NUMBER_RE.match
 15     encoding = context.encoding
 16     strict = context.strict
 17     parse_float = context.parse_float
 18     parse_int = context.parse_int
 19     parse_constant = context.parse_constant
 20     object_hook = context.object_hook
 21     object_pairs_hook = context.object_pairs_hook
 22
 23     def _scan_once(string, idx):
 24         try:
 25             nextchar = string[idx]
 26         except IndexError:
 27             raise ValueError(decoder.errmsg("Could not get the next character",string,idx))
 28             #raise StopIteration
 29
 30         if nextchar == '"':
 31             return parse_string(string, idx + 1, encoding, strict)
 32         elif nextchar == '{':
 33             return parse_object((string, idx + 1), encoding, strict,
 34                 _scan_once, object_hook, object_pairs_hook)
 35         elif nextchar == '[':
 36             return parse_array((string, idx + 1), _scan_once)
 37         elif nextchar == 'n' and string[idx:idx + 4] == 'null':
 38             return None, idx + 4
 39         elif nextchar == 't' and string[idx:idx + 4] == 'true':
 40             return True, idx + 4
 41         elif nextchar == 'f' and string[idx:idx + 5] == 'false':
 42             return False, idx + 5
 43
 44         m = match_number(string, idx)
 45         if m is not None:
 46             integer, frac, exp = m.groups()
 47             if frac or exp:
 48                 res = parse_float(integer + (frac or '') + (exp or ''))
 49             else:
 50                 res = parse_int(integer)
 51             return res, m.end()
 52         elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
 53             return parse_constant('NaN'), idx + 3
 54         elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
 55             return parse_constant('Infinity'), idx + 8
 56         elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
 57             return parse_constant('-Infinity'), idx + 9
 58         else:
 59             #raise StopIteration   # Here is where needs modification
 60             raise ValueError(decoder.errmsg("Expecting propert name enclosed in double quotes",string,idx))
 61     return _scan_once
 62
 63 make_scanner = py_make_scanner
  1. Es mejor poner la función 'make_scanner' junto con la nueva clase secundaria en un mismo archivo.
Jeremy Li
fuente
0

Simplemente pulse el mismo problema y, en mi caso, el problema estaba relacionado con BOM(marca de orden de bytes) al comienzo del archivo.

json.tool se negaría a procesar incluso el archivo vacío (solo llaves) hasta que elimine la marca UTF BOM.

Lo que he hecho es:

  • abrí mi archivo json con vim,
  • Marca de orden de bytes eliminados ( set nobomb)
  • guardar el archivo

Esto resolvió el problema con json.tool. ¡Espero que esto ayude!

Tomasz W
fuente
-1

Cuando se crea su archivo. En lugar de crear un archivo con contenido está vacío. Reemplazar con:

json.dump({}, file)
Hoang Duong
fuente
-3

Podría usar cjson , que afirma ser hasta 250 veces más rápido que las implementaciones de Python puro, dado que tiene "algún archivo JSON complicado y largo" y probablemente necesitará ejecutarlo varias veces (los decodificadores fallan e informan el primer error solo encuentro).

yahe
fuente