Visualizzazione di un messaggio di errore migliore di "Nessun oggetto JSON potrebbe essere decodificato"


128

Codice Python per caricare dati da alcuni file JSON lunghi e complicati:

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

(nota: la migliore versione del codice dovrebbe essere:

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

ma entrambi mostrano un comportamento simile)

Per molti tipi di errore JSON (delimitatori mancanti, barre rovesciate errate nelle stringhe, ecc.), Viene stampato un messaggio utile contenente il numero di riga e colonna in cui è stato rilevato l'errore JSON.

Tuttavia, per altri tipi di errore JSON (incluso il classico "utilizzo della virgola sull'ultimo elemento in un elenco", ma anche altre cose come scrivere in maiuscolo vero / falso), l'output di Python è 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

Per quel tipo di ValueError, come fai a farti capire da Python dove si trova l'errore nel file JSON?


Potresti scaricare un estratto del tuo file?
Ketouem,

Non sto cercando di trovare l'errore in un determinato file ora; Sto cercando di modificare il mio programma in modo da evidenziare l'errore in qualsiasi file futuro che legge.
GU

2
Non direttamente correlato, ma potresti semplicemente fare json.load(f)invece dijson.loads(f.read())
Martin Samson,

@OJW su quale versione di Python era presente questo comportamento?
jxramos,

Python 3.8.1 ora fornisce la posizione di errore "Valore
previsto

Risposte:


173

Ho scoperto che il simplejsonmodulo fornisce errori più descrittivi in ​​molti casi in cui il jsonmodulo integrato è vago. Ad esempio, nel caso di avere una virgola dopo l'ultimo elemento in un elenco:

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

che non è molto descrittivo. La stessa operazione con simplejson:

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

Molto meglio! Allo stesso modo per altri errori comuni come la capitalizzazione True.


18
Le versioni future di Python includeranno questi miglioramenti; è lo stesso progetto sotto.
Martijn Pieters


1
@ user2016290 La modifica diretta dei file core / pacchetto è una cattiva idea. Python è una patch scimmia facile da usare, quindi è meglio farlo nel codice.
Rebs,

2
@jxramos: L'OP ha usato Python 2.7, come risulta dal traceback. Un rapido test su ideone.com (Python 3.7.3) mostra che la jsonlibreria stdlib è stata aggiornata e fornisce il nuovo formato del messaggio di errore. Tuttavia, non ho tempo per tenere traccia delle versioni esatte in questo momento.
Martijn Pieters

1
@jxramos l'ha trovata, Python 3.5 ha aggiornato le eccezioni: bugs.python.org/issue19361 (tramite docs.python.org/3/whatsnew/3.5.html#improved-modules ).
Martijn Pieters

15

Non sarai in grado di ottenere Python per dirti dove JSON non è corretto. Dovrai usare una linter online da qualche parte come questa

Questo mostrerà un errore nel JSON che stai tentando di decodificare.


2
Esistono strumenti offline che possono farlo per i file JSON riservati?
GU

@OJW non che io sappia ma che dovrebbe risolvere il problema che stai riscontrando o almeno permetterti di risolvere il tuo problema.
myusuf3,

12
Il mio file JSON va bene: sto cercando di far stampare messaggi di errore utili al mio programma comprensibili a chiunque. Dire loro "sbarazzarsi di quella virgola alla riga 13 colonna 32" è buono. Dire loro "c'è un errore da qualche parte nel tuo file, per favore caricalo su Internet dove le persone ti aiuteranno" non va bene.
GU

7

Puoi provare la libreria rson che trovi qui: http://code.google.com/p/rson/ . Ho anche su PYPI: https://pypi.python.org/pypi/rson/0.9 in modo da poter usare easy_install o pip per ottenerlo.

per l'esempio fornito da tom:

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

RSON è progettato per essere un superset di JSON, quindi può analizzare i file JSON. Ha anche una sintassi alternativa che è molto più piacevole da guardare e modificare per gli umani. Lo uso abbastanza per i file di input.

Per quanto riguarda la capitalizzazione di valori booleani: sembra che rson legga booleani in modo errato maiuscoli come stringhe.

>>> rson.loads('[true,False]')
[True, u'False']

4

Ho avuto un problema simile ed era dovuto alle virgolette singole. Lo standard JSON ( http://json.org ) parla solo dell'uso di virgolette doppie, quindi deve essere che la jsonlibreria python supporti solo virgolette doppie.


3

Per la mia versione particolare di questo problema, sono andato avanti e ho cercato la dichiarazione di funzione load_json_file(path)all'interno del packaging.pyfile, quindi printho introdotto una riga al suo interno:

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))

In questo modo avrebbe stampato il contenuto del file json prima di inserire il try-catch e in quel modo - anche con la mia conoscenza di Python appena esistente - sono stato in grado di capire rapidamente perché la mia configurazione non era in grado di leggere il file json.
(Era perché avevo impostato il mio editor di testo per scrivere una DBA UTF-8 ... stupida)

Ne ho parlato solo perché, anche se forse non è una buona risposta al problema specifico dell'OP, questo è stato un metodo piuttosto rapido per determinare la fonte di un bug molto opprimente. E scommetto che molte persone si imbatteranno in questo articolo che sta cercando una soluzione più dettagliata per a MalformedJsonFileError: No JSON object could be decoded when reading …. In modo che potrebbe aiutarli.


È necessario utilizzare il Gestore di contesto per l'I / O dei file ( with open(fn) as f), che gestisce la chiusura del file in un'eccezione. en.wikibooks.org/wiki/Python_Programming/...
Rebs

1
+1. Se potessi mostrare un esempio del monkeypatching sul comportamento standard, sarebbe abbastanza pulito
Craig Brett

Spiacenti, non ho mai toccato alcun codice Python dopo che il problema era stato risolto. Forse qualcun'altro può aiutare?
Woodrow Shigeru

3

Quanto a me, il mio file json è molto grande, quando usa common jsonin python ottiene l'errore sopra.

Dopo l'installazione simplejsondi sudo pip install simplejson.

E poi l'ho risolto.

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()

1

Ho avuto un problema simile, questo era il mio codice:

    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

il problema era che mi ero dimenticato di file.close() averlo fatto e risolto il problema.


Ha funzionato anche per me, non so perché non ho avuto questo problema prima.
pceccon,

È necessario utilizzare il Gestore di contesto per l'I / O dei file ( with open(fn) as f), che gestisce la chiusura del file in un'eccezione. en.wikibooks.org/wiki/Python_Programming/…
Rebs

0

La risposta accettata è la più semplice per risolvere il problema. Ma nel caso in cui non ti sia permesso installare simplejson a causa della tua politica aziendale, ti propongo di seguito una soluzione per risolvere il problema particolare di "usare la virgola sull'ultimo elemento in un elenco" :

  1. Creare una classe figlio "JSONLintCheck" per ereditare dalla classe "JSONDecoder" e sovrascrivere il metodo init della classe "JSONDecoder" come di seguito:

    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 è una nuova funzione che ha usato per sovrascrivere il metodo 'scan_once' della classe sopra. Ed ecco il codice per questo :
  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. Meglio mettere la funzione 'make_scanner' insieme alla nuova classe figlio in uno stesso file.

0

Ho appena riscontrato lo stesso problema e nel mio caso il problema era correlato a BOM(contrassegno dell'ordine dei byte) all'inizio del file.

json.tool rifiuterebbe di elaborare anche file vuoti (solo parentesi graffe) fino a quando non avessi rimosso il contrassegno DBA UTF.

Quello che ho fatto è:

  • ha aperto il mio file json con vim,
  • contrassegno ordine byte rimosso ( set nobomb)
  • salvare il file

Ciò ha risolto il problema con json.tool. Spero che questo ti aiuti!


-1

Quando viene creato il tuo file. Invece di creare un file con contenuto è vuoto. Sostituirlo con:

json.dump({}, file)

-3

Potresti usare cjson , che afferma di essere fino a 250 volte più veloce delle implementazioni pure-python, dato che hai "qualche file JSON lungo complicato" e probabilmente dovrai eseguirlo più volte (i decodificatori falliscono e segnalano il primo errore che hanno solo incontro).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.