Affichage d'un meilleur message d'erreur que "Aucun objet JSON n'a pu être décodé"

128

Code Python pour charger des données à partir d'un long fichier JSON compliqué:

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

(note: la meilleure version de code devrait être:

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

mais les deux présentent un comportement similaire)

Pour de nombreux types d'erreur JSON (délimiteurs manquants, barres obliques inverses incorrectes dans les chaînes, etc.), cela imprime un joli message utile contenant le numéro de ligne et de colonne où l'erreur JSON a été trouvée.

Cependant, pour d'autres types d'erreur JSON (y compris le classique "utiliser la virgule sur le dernier élément d'une liste", mais aussi d'autres choses comme mettre en majuscule vrai / faux), la sortie de Python est simplement:

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

Pour ce type de ValueError, comment faire en sorte que Python vous dise où se trouve l'erreur dans le fichier JSON?

OJW
la source
Pourriez-vous vider un extrait de votre fichier?
Ketouem
Je n'essaye pas de trouver l'erreur dans un fichier particulier maintenant; J'essaie de modifier mon programme afin qu'il mettra en évidence l'erreur dans tout fichier futur qu'il lit.
OJW
2
Pas directement lié, mais vous pouvez simplement le faire à la json.load(f)place dejson.loads(f.read())
Martin Samson
@OJW sur quelle version de python ce comportement était-il utilisé?
jxramos
Python 3.8.1 donne maintenant la position d'erreur «Valeur attendue: ligne 1 colonne 21 (
car

Réponses:

173

J'ai trouvé que le simplejsonmodule donne des erreurs plus descriptives dans de nombreux cas où le jsonmodule intégré est vague. Par exemple, dans le cas d'une virgule après le dernier élément d'une liste:

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

ce qui n'est pas très descriptif. La même opération avec simplejson:

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

Bien mieux! De même pour d'autres erreurs courantes telles que la mise en majuscule True.

à M
la source
18
Les futures versions de Python incluront ces améliorations; c'est le même projet en dessous.
Martijn Pieters
1
@ user2016290 Modifier directement les fichiers core / package est une mauvaise idée. Python est facile à corriger, il est donc préférable de le faire dans le code.
Rebs
2
@jxramos: l'OP a utilisé Python 2.7, comme le montre le traçage. Un test rapide sur ideone.com (Python 3.7.3) montre que la jsonbibliothèque stdlib a été mise à jour et donne le nouveau format de message d'erreur. Cependant, je n'ai pas le temps de suivre les versions exactes pour le moment.
Martijn Pieters
1
@jxramos l'a trouvé, Python 3.5 a mis à jour les exceptions: bugs.python.org/issue19361 (via docs.python.org/3/whatsnew/3.5.html#improved-modules ).
Martijn Pieters
15

Vous ne pourrez pas obtenir de python pour vous dire où le JSON est incorrect. Vous devrez utiliser un linter en ligne quelque part comme celui-ci

Cela vous montrera une erreur dans le JSON que vous essayez de décoder.

myusuf3
la source
2
Existe-t-il des outils hors ligne qui peuvent le faire pour les fichiers JSON confidentiels?
OJW
@OJW pas que je sache, mais cela devrait résoudre le problème que vous rencontrez ou au moins vous permettre de réparer votre json cassé.
myusuf3
12
Mon fichier JSON va bien - j'essaie de faire en sorte que mon programme imprime des messages d'erreur utiles qui sont compréhensibles par tout le monde. Leur dire «débarrassez-vous de cette virgule à la ligne 13 colonne 32» est bien. Leur dire "il y a une erreur quelque part dans votre fichier, veuillez le télécharger sur Internet où les gens vous aideront" est mauvais.
OJW
7

Vous pouvez essayer la bibliothèque rson disponible ici: http://code.google.com/p/rson/ . Je l'ai également sur PYPI: https://pypi.python.org/pypi/rson/0.9 afin que vous puissiez utiliser easy_install ou pip pour l'obtenir.

pour l'exemple donné par tom:

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

RSON est conçu pour être un sur-ensemble de JSON, il peut donc analyser les fichiers JSON. Il a également une syntaxe alternative qui est beaucoup plus agréable à regarder et à modifier pour les humains. Je l'utilise pas mal pour les fichiers d'entrée.

Quant à la mise en majuscule des valeurs booléennes: il apparaît que rson lit les booléens en majuscules incorrectes comme des chaînes.

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

J'ai eu un problème similaire et c'était dû à des citations uniques. Le standard JSON ( http://json.org ) ne parle que de l'utilisation de guillemets doubles , il faut donc que la jsonbibliothèque python ne supporte que les guillemets doubles.

Chevalier Samar
la source
3

Pour ma version particulière de ce problème, je suis allé de l'avant et j'ai recherché la déclaration de fonction load_json_file(path)dans le packaging.pyfichier, puis j'ai introduit une printligne en contrebande :

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 cette façon, il imprimait le contenu du fichier json avant d'entrer dans le try-catch, et de cette façon - même avec mes connaissances Python à peine existantes - j'ai pu comprendre rapidement pourquoi ma configuration ne pouvait pas lire le fichier json.
(C'était parce que j'avais configuré mon éditeur de texte pour écrire une nomenclature UTF-8… stupide)

Mentionner simplement cela parce que, bien que ce ne soit peut-être pas une bonne réponse au problème spécifique du PO, c'était une méthode assez rapide pour déterminer la source d'un bogue très oppressant. Et je parie que beaucoup de gens tomberont sur cet article qui recherchent une solution plus verbeuse pour un MalformedJsonFileError: No JSON object could be decoded when reading …. Cela pourrait donc les aider.

WoodrowShigeru
la source
Vous devez utiliser le gestionnaire de contexte pour les E / S de fichier ( with open(fn) as f), il gère la fermeture du fichier dans une exception pour vous. en.wikibooks.org/wiki/Python_Programming/...
Rebs
1
+1. Si vous pouviez montrer un exemple de monkeypatching que sur le comportement standard, ce serait plutôt sympa
Craig Brett
Désolé, je n'ai jamais touché de code Python après que ce problème ait été résolu. Peut-être que quelqu'un d'autre peut vous aider?
WoodrowShigeru
3

Quant à moi, mon fichier json est très gros, lorsqu'il est utilisé jsonen python, il obtient l'erreur ci-dessus.

Après l'installation simplejsonpar sudo pip install simplejson.

Et puis je l'ai résolu.

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
la source
1

J'ai eu un problème similaire, c'était mon code:

    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

le problème était que j'avais oublié de le faire file.close() et de résoudre le problème.

Habib Kazemi
la source
A travaillé pour moi aussi, je ne sais pas pourquoi je n'ai pas eu ce problème avant.
pceccon
Vous devez utiliser le gestionnaire de contexte pour les E / S de fichier ( with open(fn) as f), il gère la fermeture du fichier dans une exception pour vous. en.wikibooks.org/wiki/Python_Programming/...
Rebs
0

La réponse acceptée est la plus simple pour résoudre le problème. Mais dans le cas où vous n'êtes pas autorisé à installer le simplejson en raison de la politique de votre entreprise, je propose ci-dessous une solution pour résoudre le problème particulier de "l'utilisation de la virgule sur le dernier élément d'une liste" :

  1. Créez une classe enfant "JSONLintCheck" pour hériter de la classe "JSONDecoder" et remplacez la méthode init de la classe "JSONDecoder" comme ci-dessous:

    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 est une nouvelle fonction qui remplaçait la méthode 'scan_once' de la classe ci-dessus. Et voici le code pour cela :
  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. Mieux vaut mettre la fonction 'make_scanner' avec la nouvelle classe enfant dans un même fichier.
Jeremy Li
la source
0

Frappez simplement le même problème et dans mon cas, le problème était lié à BOM(marque d'ordre d'octet) au début du fichier.

json.tool refuserait de traiter même un fichier vide (juste des accolades) jusqu'à ce que je supprime la marque UTF BOM.

Ce que j'ai fait est:

  • a ouvert mon fichier json avec vim,
  • marque d'ordre des octets supprimée ( set nobomb)
  • enregistrer le fichier

Cela a résolu le problème avec json.tool. J'espère que cela t'aides!

Tomasz W
la source
-1

Lorsque votre fichier est créé. Au lieu de créer un fichier avec du contenu est vide. Remplacer par:

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

Vous pouvez utiliser cjson , qui prétend être jusqu'à 250 fois plus rapide que les implémentations pur-python, étant donné que vous avez "un long fichier JSON compliqué" et que vous devrez probablement l'exécuter plusieurs fois (les décodeurs échouent et signalent la première erreur qu'ils rencontre uniquement).

yahe
la source