Сообщение об ошибке отображается лучше, чем «Невозможно декодировать объект 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
источник
Не могли бы вы сбросить отрывок из вашего файла?
Ketouem
Я сейчас не пытаюсь найти ошибку в конкретном файле; Я пытаюсь изменить свою программу, чтобы она выделяла ошибку в любом будущем файле, который она читает.
OJW
2
Не имеет прямого отношения, но вы могли бы просто сделать json.load(f)вместоjson.loads(f.read())
Мартин Самсон
@OJW, на какой версии Python было такое поведение?
jxramos
Python 3.8.1 теперь выдает позицию ошибки «Ожидаемое значение: строка 1, столбец 21 (символ 20)»
OJW

Ответы:

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: OP использовал Python 2.7, как видно из трассировки. Быстрый тест на ideone.com (Python 3.7.3) показывает, что jsonбиблиотека stdlib была обновлена ​​и дает новый формат сообщения об ошибке. Однако сейчас у меня нет времени отслеживать точные релизы.
Мартейн Питерс
1
@jxramos нашел его, Python 3.5 обновил исключения: bugs.python.org/issue19361 (через docs.python.org/3/whatsnew/3.5.html#improved-modules ).
Мартейн Питерс
15

Вы не сможете заставить python сообщать вам, где неверен JSON. Вам нужно будет использовать линтер онлайн где-то вроде этого

Это покажет вам ошибку в JSON, который вы пытаетесь декодировать.

myusuf3
источник
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, чтобы получить его.

для примера, данного томом:

>>> 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-файла перед входом в try-catch, и таким образом - даже с моими едва существующими знаниями Python - я смог быстро понять, почему моя конфигурация не могла прочитать json-файл.
(Это потому, что я настроил свой текстовый редактор для написания спецификации UTF-8… глупо)

Просто упомяну об этом, потому что, хотя это, возможно, не лучший ответ на конкретную проблему OP, это был довольно быстрый метод определения источника очень тяжелой ошибки. И я уверен, что многие люди, которые ищут более подробное решение для файла MalformedJsonFileError: No JSON object could be decoded when reading …. Так что это может им помочь.

WoodrowShigeru
источник
Вы должны использовать диспетчер контекста для файлового ввода-вывода ( with open(fn) as f), он обрабатывает закрытие файла в исключении для вас. en.wikibooks.org/wiki/Python_Programming/…
Ребс
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()
Jayhello
источник
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 01
Вы должны использовать диспетчер контекста для файлового ввода-вывода ( with open(fn) as f), он обрабатывает закрытие файла в исключении для вас. en.wikibooks.org/wiki/Python_Programming/…
Ребс
0

Принятый ответ - самый простой способ решить проблему. Но в случае, если вам не разрешено устанавливать simplejson из-за политики вашей компании, я предлагаю ниже решение, чтобы исправить конкретную проблему «использования запятой в последнем элементе списка» :

  1. Создайте дочерний класс «JSONLintCheck» для наследования от класса «JSONDecoder» и переопределите метод инициализации класса «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 откажется обрабатывать даже пустой файл (только фигурные скобки), пока я не удалю отметку UTF BOM.

Я сделал следующее:

  • открыл мой файл json с помощью vim,
  • удален знак порядка байтов (set nobomb )
  • сохранить файл

Это решило проблему с json.tool. Надеюсь это поможет!

Томаш В
источник
-1

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

json.dump({}, file)
Хоанг Дыонг
источник
-3

Вы можете использовать cjson , который утверждает, что он работает до 250 раз быстрее, чем реализация на чистом Python, учитывая, что у вас есть «какой-то длинный сложный файл JSON», и вам, вероятно, придется запускать его несколько раз (декодеры не работают и сообщают о первой ошибке, которую они только встреча).

Яхе
источник