debugging 如何诊断JSON数据中的常见错误?

uwopmtnx  于 2023-01-21  发布在  其他
关注(0)|答案(1)|浏览(167)

我不得不处理来自许多不同来源的假定JSON,而且很多时候似乎数据本身存在问题,我怀疑有时候根本就不打算使用JSON;但是很多时候它来自于一个有缺陷的工具,或者是为了快速测试而手工编写的,并且有一些错别字。
我不想询问具体的错误,而是寻找一个检查表:根据错误消息,最可能的原因是什么?这些错误消息中包含哪些信息,如何使用这些信息来定位数据中的问题?假设出于这些目的,我可以将数据保存到临时文件中以供分析(如果数据尚未来自文件)。

frebpwbc

frebpwbc1#

前言
the decoding code * 明确 * 引发的唯一异常是json.JSONDecodeError,因此异常类型无助于诊断问题。有趣的是相关消息。然而,在尝试JSON解码之前,有可能将字节解码为文本失败。这是一个超出本文范围的单独问题。
这里值得注意的是,JSON format documentation使用了与Python不同的术语,特别是{}中包含的一部分有效JSON数据是一个 object(而不是“dict”),并且包含在[]中的部分是一个 * 数组 *(而不是“list”)。当谈论文件内容时,我将使用JSON术语,当谈论解析结果或由Python代码直接创建的数据时,我将使用Python术语。
作为一般提示:使用专门的JSON查看器来检查文件,或者至少使用具有“平衡”括号功能的文本编辑器(即,假设插入指针当前位于{,它将自动找到匹配的})。

不是JSON

显示Expecting value的错误消息强烈表明数据根本不适合JSON格式。请仔细记录错误的行和列位置以获取更多信息:

  • 如果错误发生在 * 第1行第1列 *,则需要检查文件的开头。可能是数据实际上是。如果它以<开头,则当然建议XML而不是JSON。

否则,在实际JSON内容之前可能会有一些填充。有时,在Web环境中这是to implement a security restriction;在其他情况下,它是到work around a different restriction。后一种情况称为JSONP(JSON with Padding)。无论哪种方式,都需要检查数据,以确定在解析之前应该从开头(也可能是结尾)裁剪多少。

  • 其他位置可能是因为数据实际上是一些原生Python数据结构的repr,像这样的数据通常可以使用ast.literal_eval解析,但是它不应该被认为是一种实用的序列化格式-它不能很好地与非Python编写的代码互操作,并且使用repr可以很容易地产生无法通过这种方式(或任何实际方式)恢复的数据。

注意Python的原生对象表示和JSON格式之间的一些共同差异,以帮助诊断问题:

  • JSON只使用**双引号将字符串括起来;Python也可以使用单引号,以及三单引号('''example''')或三双引号("""example""")。
  • JSON使用小写的truefalse而不是TrueFalse来表示布尔值;使用null而不是None作为特殊的“这里没有任何内容”值;使用InfinityNaN而不是infnan来表示特殊的浮点值。

一个微妙之处:Expecting value还可以表示数组或对象中的尾随逗号,JSON语法不允许在列出元素或键-值对之后使用尾随逗号,尽管Python允许。尽管逗号是“额外的”,但这将被报告为缺少的东西(下一个元素或键-值对),而不是无关的东西(逗号)。
错误消息Extra data表示JSON数据末尾之后还有更多文本。

  • 如果错误发生在line 2 column 1,这强烈暗示数据实际上是JSONL(“JSON Lines”)格式--一种相关格式,其中每行输入都是一个单独的JSON实体(通常是一个对象)。只需要遍历输入的行,分别解析每一行,然后把结果放到一个列表中。例如,使用列表解析:[json.loads(line) for line in open_json_file]。有关详细信息,请参阅Loading JSONL file as JSON objects
  • 否则,额外的数据可能是JSONP填充的一部分,可以在解析之前将其删除;或者使用JSONDecoder类的.raw_decode方法:
>>> import json
>>> example = '{"key": "value"} extra'
>>> json.loads(example) # breaks because of the extra data:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.8/json/decoder.py", line 340, in decode
    raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 1 column 18 (char 17)
>>> parsed, size = json.JSONDecoder().raw_decode(example)
>>> parsed
{'key': 'value'}
>>> size # amount of text that was parsed.
16

无效的字符串文字

错误消息指出以下任何一项:

  • Invalid \\uXXXX escape
  • Invalid \\escape
  • Unterminated string starting at
  • x1米30英寸1x

表明数据中的字符串格式不正确,很可能是由于转义码编写不当。
JSON字符串不能包含严格模式下的控制代码(解析时的默认设置),例如,换行符必须用\n编码。注意,数据必须实际上包含一个反斜杠;当查看字符串形式的JSON数据的 * 表示 * 时,反斜杠将被加倍(但不是在print ing字符串时)。
JSON不接受Python的\x\U转义,只接受\u。要表示BMP之外的字符,请使用代理对:

>>> json.loads('"\\ud808\\udf45"') # encodes Unicode code point 0x12345 as a surrogate pair
'𒍅'

不像Python中的字符串常量,一个反斜杠后面跟着一些不构成有效转义序列的东西(比如空格)将not被接受:

>>> json.loads('"\\ "') # the input string has only one backslash
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.8/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.8/json/decoder.py", line 353, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Invalid \escape: line 1 column 2 (char 1)

类似地,JSON字符串中的单引号不能转义,但双引号必须转义。
在REPL调试或测试此类问题时,不要混淆JSON的转义和Python的转义,这一点很重要。

错误支架

Expecting ',' delimiterExpecting ':' delimiter意味着用于对象或数组的括号与内容不匹配。例如,像["foo": "bar"]这样的JSON几乎肯定是用来表示对象的,因此它应该包含{}而不是[]。请查看报告错误的行和字符位置,然后向后扫描到包含的括号。
但是,这些错误也可能意味着正是他们说:可能只是数组元素或键值对之间缺少逗号,或者键与其值之间缺少冒号。

无效密钥

虽然Python allows anything hashable作为dict键,但JSON需要字符串作为其对象键。这个问题由Expecting property name enclosed in double quotes表示。虽然它可能发生在手写JSON中,但它可能暗示了在Python对象上使用repr创建的数据不合适的问题。(如果在检查文件中的指定位置时,发现有人试图使用引号中的字符串键,则这种情况尤其可能发生。)
错误消息Expecting property name enclosed in double quotes * 也 * 可能表示“错误的括号”问题,特别是,如果数据应该是一个包含整数的数组,但被{}而不是[]包围,解析器将在其他任何东西之前期待一个双引号字符串键,并抱怨列表中的第一个整数。

相关问题