Відображення кращого повідомлення про помилку, ніж "Жоден об'єкт JSON не може бути декодований"


128

Код Python для завантаження даних із довгого складного файлу JSON:

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

(зверніть увагу: найкраща версія коду повинна бути:

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

але обидва проявляють подібну поведінку)

Для багатьох типів помилки JSON (відсутні роздільники, неправильні косої риски в рядках тощо) це друкує приємне корисне повідомлення, що містить номер рядка та стовпця, де була знайдена помилка JSON.

Однак для інших типів помилки JSON (включаючи класичне "використання коми в останньому елементі списку", а також інші речі, такі як використання великих букв true / false), вихід Python є просто:

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

Для цього типу ValueError, як змусити Python повідомити, де помилка у файлі JSON?


Не могли б ви скинути уривок свого файлу?
Кетоум

Я зараз не намагаюся знайти помилку в конкретному файлі; Я намагаюся змінити свою програму, щоб вона виділила помилку в будь-якому майбутньому файлі, який вона читає.
OJW

2
Не пов’язані безпосередньо, але ви можете просто зробити це json.load(f)замістьjson.loads(f.read())
Мартін Самсон

@OJW, на якій версії python була така поведінка?
jxramos

Python 3.8.1 тепер дає позицію помилки "Очікує значення: рядок 1, стовпець 21 (
графік

Відповіді:


173

Я виявив, що simplejsonмодуль дає більше описових помилок у багатьох випадках, коли вбудований jsonмодуль розпливчастий. Наприклад, у випадку наявності коми після останнього елемента в списку:

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

що не дуже описово. Та сама операція з simplejson:

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

Набагато краще! Так само і для інших поширених помилок, таких як використання великих букв True.


18
Майбутні версії Python включатимуть ці вдосконалення; це той самий проект під ним.
Martijn Pieters


1
@ user2016290 Відредагувати основні / пакувальні файли безпосередньо - це погана ідея. Python легко мавповий виправлення, тому краще зробити це в коді.
Rebs

2
@jxramos: ОП використовував Python 2.7, як видно з простеження. Швидкий тест на ideone.com (Python 3.7.3) показує, що jsonбібліотека stdlib була оновлена ​​та дає новий формат повідомлення про помилку. Однак у мене немає часу відстежувати точні випуски.
Martijn Pieters

1
@jxramos знайшов це, Python 3.5 оновив винятки: bugs.python.org/issue19361 (через docs.python.org/3/whatsnew/3.5.html#improved-modules ).
Martijn Pieters

15

Ви не зможете отримати python, щоб сказати вам, де JSON неправильний. Вам потрібно буде використовувати Лінтера в Інтернеті де - то , як це

Це покаже вам помилку в JSON, яку ви намагаєтесь розшифрувати.


2
Чи є офлайн-інструменти, які можуть це зробити для конфіденційних файлів JSON?
OJW

@OJW не те, про що я знаю, але це повинно вирішити проблему, яка виникає у вас, або, принаймні, дозволить виправити ваш зламаний json.
myusuf3

12
Мій файл JSON добре - я намагаюся зробити свою програму друком корисних повідомлень про помилки, зрозумілих нікому. Сказати їм "позбутися цієї коми через рядок 13 стовпця 32" - це добре. Скажіть їм, що "десь у вашому файлі є помилка, будь ласка, завантажте його в Інтернет, де люди допоможуть вам", це погано.
OJW

7

Ви можете спробувати бібліотеку rson, яку ви знайдете тут: http://code.google.com/p/rson/ . Я також на PYPI: https://pypi.python.org/pypi/rson/0.9, щоб ви могли використовувати easy_install або pip, щоб отримати його.

для прикладу, поданого tom:

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

RSON призначений для суперсети JSON, тому він може аналізувати файли JSON. Він також має альтернативний синтаксис, який людям набагато приємніше переглядати та редагувати. Я використовую його досить небагато для введення файлів.

Що стосується великої літери булевих значень: виявляється, що rson читає неправильно прописані великі булеві букви як рядки.

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

4

У мене була подібна проблема, і це було пов’язано з однозначними цитатами. Стандарт JSON ( http://json.org ) говорить лише про використання подвійних лапок, тому jsonбібліотека python повинна підтримувати лише подвійні лапки.


3

Для моєї конкретної версії цієї проблеми я пішов вперед і шукав декларацію функції load_json_file(path)у packaging.pyфайлі, а потім переправляв printрядок у нього:

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

Таким чином він надрукував би вміст файлу json перед тим, як ввести пробний лов, і таким чином - навіть маючи ледь існуючі знання Python - я зміг швидко зрозуміти, чому моя конфігурація не може прочитати файл json.
(Це було тому, що я налаштував свій текстовий редактор, щоб написати UTF-8 BOM ... дурний)

Зазначимо це лише тому, що, хоча, можливо, не є гарною відповіддю на конкретну проблему ОП, це був досить швидкий метод визначення джерела дуже гнітючої помилки. І я сумніваюся, що багато людей наткнуться на цю статтю, які шукають більш детальне рішення для MalformedJsonFileError: No JSON object could be decoded when reading …. Тож це може їм допомогти.


Ви повинні використовувати контекстний менеджер для файлів I / O ( with open(fn) as f), він обробляє закриття файлу за винятком для вас. en.wikibooks.org/wiki/Python_Programming/…
Rebs

1
+1. Якщо ви могли б показати приклад того, як мавпа привертає це до стандартної поведінки, це було б досить акуратно
Крейг Бретт

Вибачте, я жодного разу не торкався жодного коду Python після того, як з'ясувалася ця проблема. Може, хтось ще може допомогти?
WoodrowShigeru

3

Як на мене, мій файл json дуже великий, при використанні звичайного jsonв python він отримує вказану вище помилку.

Після установки з simplejsonдопомогою sudo pip install simplejson.

І тоді я це вирішив.

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

У мене була подібна проблема, це мій код:

    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

Проблема була, я забув, що file.close() я це зробив, і вирішив проблему.


Працював і для мене, не знаю, чому раніше не було цієї проблеми.
pceccon

Ви повинні використовувати контекстний менеджер для файлів I / O ( with open(fn) as f), він обробляє закриття файлу за винятком для вас. en.wikibooks.org/wiki/Python_Programming/…
Rebs

0

Прийнята відповідь є найпростішим, щоб виправити проблему. Але якщо вам не дозволяється встановлювати simplejson через політику вашої компанії, пропоную нижче рішення, щоб виправити конкретну проблему "використання коми на останньому елементі у списку" :

  1. Створіть дочірній клас "JSONLintCheck" для успадкування від класу "JSONDecoder" та замініть метод init класу "JSONDecoder", як показано нижче:

    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 - це нова функція, яка використовується для заміщення методу 'scan_once' вищевказаного класу. І ось код для цього:
  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. Краще покладіть функцію 'make_scanner' разом із новим дочірнім класом у той самий файл.

0

Просто натисніть ту саму проблему, і в моєму випадку проблема була пов’язана з BOM(байт порядку відмітки) на початку файлу.

json.tool відмовився б обробляти навіть порожній файл (лише фігурні дужки), поки я не зняв позначку BT UTF.

Що я зробив:

  • відкрив мій файл json з vim,
  • видалено позначку порядку байту ( set nobomb)
  • зберегти файл

Це вирішило проблему з json.tool. Сподіваюся, це допомагає!


-1

Коли ваш файл створений. Замість створення файлу із вмістом порожній. Замінити:

json.dump({}, file)

-3

Ви можете використовувати cjson , який стверджує, що до 250 разів швидше, ніж реалізація pure-python, враховуючи, що у вас є "якийсь довгий складний файл JSON", і вам, ймовірно, доведеться запустити його кілька разів (дешифратори виходять з ладу і повідомляють про першу помилку. зустріти лише).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.