1
"""Implementation of JSONDecoder
5
from json import scanner
7
from _json import scanstring as c_scanstring
11
__all__ = ['JSONDecoder', 'JSONDecodeError']
13
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
17
NegInf = float('-inf')
20
class JSONDecodeError(ValueError):
21
"""Subclass of ValueError with the following additional properties:
23
msg: The unformatted error message
24
doc: The JSON document being parsed
25
pos: The start index of doc where parsing failed
26
lineno: The line corresponding to pos
27
colno: The column corresponding to pos
30
# Note that this exception is used from _json
31
def __init__(self, msg, doc, pos):
32
lineno = doc.count('\n', 0, pos) + 1
33
colno = pos - doc.rfind('\n', 0, pos)
34
errmsg = '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
35
ValueError.__init__(self, errmsg)
43
return self.__class__, (self.msg, self.doc, self.pos)
46
_CONSTANTS = frozendict({
53
HEXDIGITS = re.compile(r'[0-9A-Fa-f]{4}', FLAGS)
54
STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
56
'"': '"', '\\': '\\', '/': '/',
57
'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t',
60
def _decode_uXXXX(s, pos, _m=HEXDIGITS.match):
64
return int(esc.group(), 16)
67
msg = "Invalid \\uXXXX escape"
68
raise JSONDecodeError(msg, s, pos)
70
def py_scanstring(s, end, strict=True,
71
_b=BACKSLASH, _m=STRINGCHUNK.match):
72
"""Scan the string s for a JSON string. End is the index of the
73
character in s after the quote that started the JSON string.
74
Unescapes all valid JSON string escape sequences and raises ValueError
75
on attempt to decode an invalid string. If strict is False then literal
76
control characters are allowed in the string.
78
Returns a tuple of the decoded string and the index of the character in s
79
after the end quote."""
81
_append = chunks.append
86
raise JSONDecodeError("Unterminated string starting at", s, begin)
88
content, terminator = chunk.groups()
89
# Content is contains zero or more unescaped string characters
92
# Terminator is the end of string, a literal control character,
93
# or a backslash denoting that an escape sequence follows
96
elif terminator != '\\':
98
#msg = "Invalid control character %r at" % (terminator,)
99
msg = "Invalid control character {0!r} at".format(terminator)
100
raise JSONDecodeError(msg, s, end - 1)
107
raise JSONDecodeError("Unterminated string starting at",
109
# If not a unicode escape sequence, must be in the lookup table
114
msg = "Invalid \\escape: {0!r}".format(esc)
115
raise JSONDecodeError(msg, s, end)
118
uni = _decode_uXXXX(s, end)
120
if 0xd800 <= uni <= 0xdbff and s[end:end + 2] == '\\u':
121
uni2 = _decode_uXXXX(s, end + 1)
122
if 0xdc00 <= uni2 <= 0xdfff:
123
uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
127
return ''.join(chunks), end
130
# Use speedup if available
131
scanstring = c_scanstring or py_scanstring
133
WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
134
WHITESPACE_STR = ' \t\n\r'
137
def JSONObject(s_and_end, strict, scan_once, object_hook, object_pairs_hook,
138
memo=None, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
141
pairs_append = pairs.append
142
# Backwards compatibility
145
memo_get = memo.setdefault
146
# Use a slice to prevent IndexError from being raised, the following
147
# check will raise a more specific ValueError if the string is empty
148
nextchar = s[end:end + 1]
149
# Normally we expect nextchar == '"'
152
end = _w(s, end).end()
153
nextchar = s[end:end + 1]
154
# Trivial empty object
156
if object_pairs_hook is not None:
157
result = object_pairs_hook(pairs)
158
return result, end + 1
160
if object_hook is not None:
161
pairs = object_hook(pairs)
162
return pairs, end + 1
163
elif nextchar != '"':
164
raise JSONDecodeError(
165
"Expecting property name enclosed in double quotes", s, end)
168
key, end = scanstring(s, end, strict)
169
key = memo_get(key, key)
170
# To skip some function call overhead we optimize the fast paths where
171
# the JSON key separator is ": " or just ":".
172
if s[end:end + 1] != ':':
173
end = _w(s, end).end()
174
if s[end:end + 1] != ':':
175
raise JSONDecodeError("Expecting ':' delimiter", s, end)
182
end = _w(s, end + 1).end()
187
value, end = scan_once(s, end)
188
except StopIteration as err:
189
raise JSONDecodeError("Expecting value", s, err.value) from None
190
pairs_append((key, value))
194
end = _w(s, end + 1).end()
202
elif nextchar != ',':
203
raise JSONDecodeError("Expecting ',' delimiter", s, end - 1)
205
end = _w(s, end).end()
206
nextchar = s[end:end + 1]
210
raise JSONDecodeError("Illegal trailing comma before end of object", s, comma_idx)
211
raise JSONDecodeError(
212
"Expecting property name enclosed in double quotes", s, end - 1)
213
if object_pairs_hook is not None:
214
result = object_pairs_hook(pairs)
217
if object_hook is not None:
218
pairs = object_hook(pairs)
221
def JSONArray(s_and_end, scan_once, array_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
224
nextchar = s[end:end + 1]
226
end = _w(s, end + 1).end()
227
nextchar = s[end:end + 1]
228
# Look-ahead for trivial empty array
230
if array_hook is not None:
231
values = array_hook(values)
232
return values, end + 1
233
_append = values.append
236
value, end = scan_once(s, end)
237
except StopIteration as err:
238
raise JSONDecodeError("Expecting value", s, err.value) from None
240
nextchar = s[end:end + 1]
242
end = _w(s, end + 1).end()
243
nextchar = s[end:end + 1]
247
elif nextchar != ',':
248
raise JSONDecodeError("Expecting ',' delimiter", s, end - 1)
254
end = _w(s, end + 1).end()
255
nextchar = s[end:end + 1]
259
raise JSONDecodeError("Illegal trailing comma before end of array", s, comma_idx)
261
if array_hook is not None:
262
values = array_hook(values)
266
class JSONDecoder(object):
267
"""Simple JSON <https://json.org> decoder
269
Performs the following translations in decoding by default:
271
+---------------+-------------------+
273
+===============+===================+
275
+---------------+-------------------+
277
+---------------+-------------------+
279
+---------------+-------------------+
280
| number (int) | int |
281
+---------------+-------------------+
282
| number (real) | float |
283
+---------------+-------------------+
285
+---------------+-------------------+
287
+---------------+-------------------+
289
+---------------+-------------------+
291
It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
292
their corresponding ``float`` values, which is outside the JSON spec.
296
def __init__(self, *, object_hook=None, parse_float=None,
297
parse_int=None, parse_constant=None, strict=True,
298
object_pairs_hook=None, array_hook=None):
299
"""``object_hook``, if specified, will be called with the result
300
of every JSON object decoded and its return value will be used in
301
place of the given ``dict``. This can be used to provide custom
302
deserializations (e.g. to support JSON-RPC class hinting).
304
``object_pairs_hook``, if specified will be called with the result
305
of every JSON object decoded with an ordered list of pairs. The
306
return value of ``object_pairs_hook`` will be used instead of the
307
``dict``. This feature can be used to implement custom decoders.
308
If ``object_hook`` is also defined, the ``object_pairs_hook`` takes
311
``array_hook`` is an optional function that will be called with the
312
result of any literal array decode (a ``list``). The return value of
313
this function will be used instead of the ``list``. This feature can
314
be used along ``object_pairs_hook`` to customize the resulting data
315
structure - for example, by setting that to ``frozendict`` and
316
``array_hook`` to ``tuple``, one can get a deep immutable data
317
structute from any JSON data.
319
``parse_float``, if specified, will be called with the string
320
of every JSON float to be decoded. By default this is equivalent to
321
float(num_str). This can be used to use another datatype or parser
322
for JSON floats (e.g. decimal.Decimal).
324
``parse_int``, if specified, will be called with the string
325
of every JSON int to be decoded. By default this is equivalent to
326
int(num_str). This can be used to use another datatype or parser
327
for JSON integers (e.g. float).
329
``parse_constant``, if specified, will be called with one of the
330
following strings: -Infinity, Infinity, NaN.
331
This can be used to raise an exception if invalid JSON numbers
334
If ``strict`` is false (true is the default), then control
335
characters will be allowed inside strings. Control characters in
336
this context are those with character codes in the 0-31 range,
337
including ``'\\t'`` (tab), ``'\\n'``, ``'\\r'`` and ``'\\0'``.
339
self.object_hook = object_hook
340
self.parse_float = parse_float or float
341
self.parse_int = parse_int or int
342
self.parse_constant = parse_constant or _CONSTANTS.__getitem__
344
self.object_pairs_hook = object_pairs_hook
345
self.array_hook = array_hook
346
self.parse_object = JSONObject
347
self.parse_array = JSONArray
348
self.parse_string = scanstring
350
self.scan_once = scanner.make_scanner(self)
353
def decode(self, s, _w=WHITESPACE.match):
354
"""Return the Python representation of ``s`` (a ``str`` instance
355
containing a JSON document).
358
135
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
359
end = _w(s, end).end()
361
raise JSONDecodeError("Extra data", s, end)
364
def raw_decode(self, s, idx=0):
365
"""Decode a JSON document from ``s`` (a ``str`` beginning with
366
a JSON document) and return a 2-tuple of the Python
367
representation and the index in ``s`` where the document ended.
369
This can be used to decode a JSON document from a string that may
370
have extraneous data at the end.
374
135
135
obj, end = self.scan_once(s, idx)
375
except StopIteration as err:
376
raise JSONDecodeError("Expecting value", s, err.value) from None