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

Posted

技术标签:

【中文标题】显示比“无法解码 JSON 对象”更好的错误消息【英文标题】:Displaying better error message than "No JSON object could be decoded" 【发布时间】:2013-01-31 17:04:48 【问题描述】:

从一些长而复杂的 JSON 文件中加载数据的 Python 代码:

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

【问题讨论】:

您可以转储您的文件的摘录吗? 我现在不想在特定文件中查找错误;我正在尝试修改我的程序,以便它在以后读取的任何文件中突出显示错误。 没有直接关系,但您可以使用json.load(f) 而不是json.loads(f.read()) @OJW 这个行为是在哪个版本的 python 上发生的? Python 3.8.1 现在给出错误位置“期望值:第 1 行第 21 列(字符 20)” 【参考方案1】:

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

【讨论】:

Python 的未来版本将包括这些改进;下面是同一个项目。 ***.com/questions/718040/… @user2016290 直接编辑核心/包文件是个坏主意。 Python 很容易修改补丁,所以最好在代码中完成。 @jxramos:OP 使用 Python 2.7,从回溯中可以明显看出。快速的test on ideone.com (Python 3.7.3) 显示stdlib json 库已更新并提供新的错误消息格式。不过,我现在没有时间跟踪确切的版本。 @jxramos 找到了,Python 3.5 更新了异常:bugs.python.org/issue19361(通过docs.python.org/3/whatsnew/3.5.html#improved-modules)。【参考方案2】:

您将无法让 python 告诉您 JSON 哪里不正确。您将需要在线使用 linter,例如 this

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

【讨论】:

是否有离线工具可以对机密 JSON 文件执行此操作? @OJW 我不知道,但这应该可以解决您遇到的问题,或者至少可以让您修复损坏的 json。 我的 JSON 文件很好 - 我正在尝试让我的程序打印任何人都可以理解的有用错误消息。告诉他们“去掉第 13 行第 32 列的那个逗号”很好。告诉他们“您的文件中某处有错误,请将其上传到人们会帮助您的互联网上”是不好的。【参考方案3】:

你可以试试这里的 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)只讨论使用双引号,所以一定是pythonjson库只支持双引号。

【讨论】:

【参考方案5】:

对于这个问题的特定版本,我继续在packaging.py 文件中搜索load_json_file(path) 的函数声明,然后将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 特定问题的一个很好的答案,但这是确定一个非常令人讨厌的错误的来源的一种相当快速的方法。我敢打赌,很多人会偶然发现这篇文章,他们正在为MalformedJsonFileError: No JSON object could be decoded when reading … 寻找更详细的解决方案。所以这可能会对他们有所帮助。

【讨论】:

您应该使用上下文管理器进行文件 I/O (with open(fn) as f),它会在异常情况下为您处理关闭文件。 en.wikibooks.org/wiki/Python_Programming/… +1。如果您可以展示一个将猴子修补到标准行为上的示例,那就太好了 抱歉,在解决这个问题后,我再也没有接触过任何 Python 代码。也许其他人可以提供帮助?【参考方案6】:

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

通过sudo pip install simplejson安装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()

【讨论】:

【参考方案7】:

我遇到了类似的问题,这是我的代码:

    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/O (with open(fn) as f),它会在异常情况下为您处理关闭文件。 en.wikibooks.org/wiki/Python_Programming/…【参考方案8】:

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

    创建一个子类“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)
    
    ma​​ke_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
    最好将“make_scanner”函数与新的子类放在同一个文件中。

【讨论】:

【参考方案9】:

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

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

我所做的是:

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

这解决了 json.tool 的问题。希望这会有所帮助!

【讨论】:

【参考方案10】:

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

json.dump(, file)

【讨论】:

【参考方案11】:

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

【讨论】:

以上是关于显示比“无法解码 JSON 对象”更好的错误消息的主要内容,如果未能解决你的问题,请参考以下文章

如何修复“JSONDecodeError:无法解码 JSON 对象:第 1 行第 0 列(字符 0)”?

sql server利用不同语种语言显示报错错误消息的方法示例

消息队列触发器的可伸缩性是不是比 Http 触发器更好?

(转)MySQL常见报错类型

在 bash 中退出并显示错误消息(单行)

为啥抛出异常比返回错误代码更好?