Reputation: 10417
To load a json, I use the json.loads()
method with a try/except
block to get where the error has been occured if it fail.
I am getting errors, in the form:
Expecting object: line 1 column 14117248 (char 14117247)
I am asking if there is a way to get the line/char pointed by this error.
Is there a way in python that return the line X column Y
from a string?
Upvotes: 3
Views: 4324
Reputation: 1124010
From Python 3.5 onwards, decoding can raise an JSONDecodeError
exception. This exception contains a few extra attributes, use those to print out some context. I'd limit this to a small string. Your sample input string has at least 14117247 characters on the first line, probably because your input contains no line separators at all; it is probably not all that helpful to print all those characters:
import json
try:
json.loads(some_string)
except json.JSONDecodeError as err:
# grab a reasonable section, say 40 characters.
start, stop = max(0, err.pos - 20), err.pos + 20
snippet = err.doc[start:stop]
print(err)
print('... ' if start else '', snippet, ' ...' if stop < len(err.doc) else '', sep="")
print('^'.rjust(21 if not start else 25))
This outputs 40 characters of context (with ellipsis to indicate where we cut the string short) and adds a ^
caret underneath the indicated position:
>>> import random, json
>>> some_string = json.dumps([{"foo": "bar"}] * 1_000_000)
>>> error_pos = random.randrange(len(some_string))
>>> error_pos = some_string.find(",", error_pos) # to avoid injecting an error inside a string
>>> some_string = some_string[:error_pos] + "!" + some_string[error_pos:]
>>> try:
... json.loads(some_string)
... except json.JSONDecodeError as err:
... # grab a reasonable section, say 40 characters.
... start, stop = max(0, err.pos - 20), err.pos + 20
... snippet = err.doc[start:stop]
... print(err)
... print('... ' if start else '', snippet, ' ...' if stop < len(err.doc) else '', sep="")
... print('^'.rjust(21 if not start else 25))
...
Expecting ',' delimiter: line 1 column 14153440 (char 14153439)
... ar"}, {"foo": "bar"}!, {"foo": "bar"}, { ...
^
In Python 3.4 and earlier, a ValueError
exception is raised. You can parse the message (the exception.args[0]
value) for clues, but this requires manual string parsing. See the errmsg()
function; reverse its formatting when parsing.
For this specific case however (where a value is expected), you'll need 3.4 or newer to get a line and column indicator; before 3.4 the best the module could do was give you the unhelpful message No JSON object could be decoded.
Upvotes: 7
Reputation: 1155
Most likely, no. At least in Python 2.7. loads
does not provide any means for customizing how it behaves on errors.
try:
value, end = scan_once(s, end)
except StopIteration:
raise ValueError(errmsg("Expecting object", s, end))
But you can parse the location from the error message to read the line from your input. The error format can be either {0}: line {1} column {2} (char {3})
or {0}: line {1} column {2} - line {3} column {4} (char {5} - {6})
.
Upvotes: 2