When I use json.loads in Python 3 and catch any resulting errors, like:
try:
data = json.loads(string)
except ValueError as err:
print(err)
I get a helpful message like:
Expecting ',' delimiter: line 12 column 12 (char 271)
I would like to be able to display this to the user, along with exactly the location which is causing the problem (I am reading in user-written JSON). How can I get out the line and column?
I could use a regex on err, but that feels like a bad idea, as I don't know if this message is internationalised, and could change in different versions of python. Is there a better way?
If you use simplejson library, you get a well qualified JSONDecodeError
:
class JSONDecodeError(ValueError):
"""Subclass of ValueError with the following additional properties:
msg: The unformatted error message
doc: The JSON document being parsed
pos: The start index of doc where parsing failed
end: The end index of doc where parsing failed (may be None)
lineno: The line corresponding to pos
colno: The column corresponding to pos
endlineno: The line corresponding to end (may be None)
endcolno: The column corresponding to end (may be None)
"""
Hopefully, this will be merged into stdlib soon.
if json object is small, Past your Json object here http://jsonlint.com/ it gives where json breaks.
[This answer is outdated. See other answers for modern python versions]
Scanning the json/decoder.py source code, we can see that the decoder's error messages are constructed using the errmsg
function:
def errmsg(msg, doc, pos, end=None):
# Note that this function is called from _json
lineno, colno = linecol(doc, pos)
if end is None:
fmt = '{0}: line {1} column {2} (char {3})'
return fmt.format(msg, lineno, colno, pos)
#fmt = '%s: line %d column %d (char %d)'
#return fmt % (msg, lineno, colno, pos)
endlineno, endcolno = linecol(doc, end)
fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
#fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
#return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
Since this is a pure-python module, it's easy to wrap this function with a custom one. This process is known as monkey patching:
import json
original_errmsg= json.decoder.errmsg
def our_errmsg(msg, doc, pos, end=None):
json.last_error_position= json.decoder.linecol(doc, pos)
return original_errmsg(msg, doc, pos, end)
json.decoder.errmsg= our_errmsg
try:
data = json.loads('{1:}')
except ValueError as e:
print("error at", json.last_error_position)
Obviously, this solution is not ideal, since the implementation may change at any time, although it's still better than relying on the message. You should check if errmsg
exists before patching (and possibly if there's no other arguments, or use varargs).
In Python 3.5 and up, a specialized JSONDecodeError
will be raised instead of ValueError
. It has several useful attributes - quoting from the documentation:
msg
: The unformatted error message.doc
: The JSON document being parsed.pos
: The start index ofdoc
where parsing failed.lineno
: The line corresponding topos
.colno
: The column corresponding topos
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With