How can I locate where in a JSON file a key is located?
Question:
I’m writing a parser that uses a JSON file for configuration, and if it detects an invalid value, I want to be able to output the location of the problematic data in the form of the line and column number in the source file where the error occurred. I’ve set up all of the tracing and have a path to the problematic data in the form of a list, where each item is either a key to an object or an array index, like ["foo", "bar", 3, "baz"]
, but can’t figure out how to locate where in the file the path is.
Ideally, the error handling code should be able to print this:
ValidationError: Features require a "feature"
at .features[0]: "theme.json" line 6, column 13
I found that using a custom JSONDecoder
lets you hook into the processing of objects through JSONDecoder.object_hook
but the information given to that function is not sufficient and there isn’t any hook for arrays, either.
If needed, this is the error class:
class JSONTraceable(Exception):
def __init__(
self,
message: str,
json_path: typing.Union[str, typing.List[typing.Union[str, int]]] = "",
):
super().__init__(message)
if isinstance(json_path, str):
self.json_path = [json_path]
elif isinstance(json_path, list):
self.json_path = json_path
else:
raise TypeError(
f"JSON path should be a string or a list, not {type(json_path).__name__}"
)
def extend(self, key: typing.Union[str, int]):
self.json_path.insert(0, key)
Answers:
I solved the problem by writing my own JSON parser that keeps track of the location of everything as it runs, following the spec available at json.org. If you want to have a look at the full code for the parser, it’s on GitHub: github permalink
I’m writing a parser that uses a JSON file for configuration, and if it detects an invalid value, I want to be able to output the location of the problematic data in the form of the line and column number in the source file where the error occurred. I’ve set up all of the tracing and have a path to the problematic data in the form of a list, where each item is either a key to an object or an array index, like ["foo", "bar", 3, "baz"]
, but can’t figure out how to locate where in the file the path is.
Ideally, the error handling code should be able to print this:
ValidationError: Features require a "feature"
at .features[0]: "theme.json" line 6, column 13
I found that using a custom JSONDecoder
lets you hook into the processing of objects through JSONDecoder.object_hook
but the information given to that function is not sufficient and there isn’t any hook for arrays, either.
If needed, this is the error class:
class JSONTraceable(Exception):
def __init__(
self,
message: str,
json_path: typing.Union[str, typing.List[typing.Union[str, int]]] = "",
):
super().__init__(message)
if isinstance(json_path, str):
self.json_path = [json_path]
elif isinstance(json_path, list):
self.json_path = json_path
else:
raise TypeError(
f"JSON path should be a string or a list, not {type(json_path).__name__}"
)
def extend(self, key: typing.Union[str, int]):
self.json_path.insert(0, key)
I solved the problem by writing my own JSON parser that keeps track of the location of everything as it runs, following the spec available at json.org. If you want to have a look at the full code for the parser, it’s on GitHub: github permalink