比“无法解码JSON对象”显示更好的错误消息

问题:比“无法解码JSON对象”显示更好的错误消息

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文件中的错误在哪里?

Python code to load data from some long complicated JSON file:

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

(note: the best code version should be:

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

but both exhibit similar behavior)

For many types of JSON error (missing delimiters, incorrect backslashes in strings, etc), this prints a nice helpful message containing the line and column number where the JSON error was found.

However, for other types of JSON error (including the classic “using comma on the last item in a list”, but also other things like capitalising true/false), Python’s output is just:

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

For that type of ValueError, how do you get Python to tell you where is the error in the JSON file?


回答 0

我发现,在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

I’ve found that the simplejson module gives more descriptive errors in many cases where the built-in json module is vague. For instance, for the case of having a comma after the last item in a list:

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

which is not very descriptive. The same operation with simplejson:

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

Much better! Likewise for other common errors like capitalizing True.


回答 1

您将无法获得python来告诉您JSON不正确的地方。您将需要在这样的地方在线使用棉绒

这将向您显示您尝试解码的JSON错误。

You wont be able to get python to tell you where the JSON is incorrect. You will need to use a linter online somewhere like this

This will show you error in the JSON you are trying to decode.


回答 2

您可以尝试在以下位置找到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']

You could try the rson library found here: http://code.google.com/p/rson/ . I it also up on PYPI: https://pypi.python.org/pypi/rson/0.9 so you can use easy_install or pip to get it.

for the example given by tom:

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

RSON is a designed to be a superset of JSON, so it can parse JSON files. It also has an alternate syntax which is much nicer for humans to look at and edit. I use it quite a bit for input files.

As for the capitalizing of boolean values: it appears that rson reads incorrectly capitalized booleans as strings.

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

回答 3

我有一个类似的问题,这是由于单引号引起的。JSON标准(http://json.org)仅讨论使用双引号,因此必须是python json库仅支持双引号。

I had a similar problem and it was due to singlequotes. The JSON standard(http://json.org) talks only about using double quotes so it must be that the python json library supports only double quotes.


回答 4

对于这个问题的特定版本,我继续搜索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))

这样,它将在进入try-catch之前打印json文件的内容,并且即使我几乎不具备Python知识,我也能够迅速弄清楚为什么我的配置无法读取json文件。
(这是因为我设置了文本编辑器来编写UTF-8 BOM …愚蠢)

仅仅提及这一点是因为,虽然可能不能很好地解决OP的特定问题,但这是一种确定非常令人讨厌的bug的来源的相当快捷的方法。我敢打赌,很多人会偶然发现这篇文章,他们正在寻找更详细的解决方案MalformedJsonFileError: No JSON object could be decoded when reading …。这样可能对他们有帮助。

For my particular version of this problem, I went ahead and searched the function declaration of load_json_file(path) within the packaging.py file, then smuggled a print line into it:

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

That way it would print the content of the json file before entering the try-catch, and that way – even with my barely existing Python knowledge – I was able to quickly figure out why my configuration couldn’t read the json file.
(It was because I had set up my text editor to write a UTF-8 BOM … stupid)

Just mentioning this because, while maybe not a good answer to the OP’s specific problem, this was a rather quick method in determining the source of a very oppressing bug. And I bet that many people will stumble upon this article who are searching a more verbose solution for a MalformedJsonFileError: No JSON object could be decoded when reading …. So that might help them.


回答 5

对我来说,我的json文件很大,json在python中使用common 时会出现上述错误。

安装后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()

As to me, my json file is very large, when use common json in python it gets the above error.

After install simplejson by sudo pip install simplejson.

And then I solved it.

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

回答 6

我有一个类似的问题,这是我的代码:

    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() 做到这一点并解决了问题。

I had a similar problem this was my 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

the problem was i had forgotten to file.close() I did it and fixed the problem.


回答 7

可接受的答案是解决问题的最简单方法。但是,如果由于公司政策而不允许您安装simplejson,我建议采用以下解决方案来解决“在列表中的最后一项上使用逗号”这一特定问题:

  1. 创建一个子类“ JSONLintCheck”以从类“ JSONDecoder”继承,并覆盖类“ JSONDecoder”的init方法,如下所示:

    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”功能与新的子类一起放入同一文件中。

The accepted answer is the easiest one to fix the problem. But in case you are not allowed to install the simplejson due to your company policy, I propose below solution to fix the particular issue of “using comma on the last item in a list”:

  1. Create a child class “JSONLintCheck” to inherite from class “JSONDecoder” and override the init method of the class “JSONDecoder” like below:

    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 is a new function that used to override the ‘scan_once’ method of the above class. And here is code for it:
  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. Better put the ‘make_scanner’ function together with the new child class into a same file.

回答 8

只是遇到了同样的问题,在我的情况下,问题与BOM文件开头的(字节顺序标记)有关。

json.tool 直到我删除了UTF BOM标记,都拒绝处理甚至是空文件(只是花括号)。

我所做的是:

  • 用vim打开我的json文件,
  • 删除了字节顺序标记(set nobomb
  • 保存存档

这就解决了json.tool的问题。希望这可以帮助!

Just hit the same issue and in my case the problem was related to BOM (byte order mark) at the beginning of the file.

json.tool would refuse to process even empty file (just curly braces) until i removed the UTF BOM mark.

What I have done is:

  • opened my json file with vim,
  • removed byte order mark (set nobomb)
  • save file

This resolved the problem with json.tool. Hope this helps!


回答 9

创建文件时。而不是创建内容为空的文件。用。。。来代替:

json.dump({}, file)

When your file is created. Instead of creating a file with content is empty. Replace with:

json.dump({}, file)

回答 10

您可以使用cjson,它声称比纯python实现快250倍,因为您有“某个较长且复杂的JSON文件”,并且可能需要运行几次(解码器失败并报告第一个错误)仅遇到)。

You could use cjson, that claims to be up to 250 times faster than pure-python implementations, given that you have “some long complicated JSON file” and you will probably need to run it several times (decoders fail and report the first error they encounter only).