Description
Rationale
The JSONVisitor
is quite useful for validating the content of a JSON document (and getting locations of incorrect data). However, currently it is rather cumbersome to find out at which JSON path in the document the parser is when a visitor function is called.
Suggestion
It would therefore be good if some of the JSONVisitor
functions had an extra parameter for the JSONPath
of the currently visited element:
onObjectBegin
,onArrayBegin
,onLiteralValue
onObjectProperty
: Here theproperty
which is visited should probably not be part of the JSON path
For the other functions adding a JSONPath
would probably either not add much value (onObjectEnd
, onSeparator
, onArrayEnd
), or its behavior cannot be defined very well (onComment
, onError
).
However, because JSONPath
is a mutable array (and is therefore modified when the parser proceeds), it would be sanest to only hand out copies of it to the visitor functions. A performant way in which this could be implemented is to have a supplier function of JSON path so the copy is only created when the user actually uses the JSON path. For example:
export interface JSONVisitor {
onObjectBegin?: (
offset: number,
length: number,
startLine: number,
startCharacter: number,
pathSupplier: () => JSONPath
) => void;
...
}
Based on the TypeScript documentation adding an extra function parameter should not be an issue.
What do yo think?
Workaround
Users implementing JSONVisitor
can manually maintain a JSON path array, updating it in the respective visitor functions.
Demo implementation (might contain bugs)
const path = []
jsoncParser.visit(jsonContent, {
onError(error, offset, length, startLine, startCharacter) {
// ... error handling
},
onArrayBegin() {
path.push(0)
},
onArrayEnd() {
path.pop()
},
onSeparator() {
// Check if inside of array (and therefore path element is array index)
if (typeof path[path.length - 1] === "number") {
path[path.length - 1]++
}
},
onObjectBegin() {
// Push temporary placeholder for object property names
path.push(undefined)
},
onObjectEnd() {
path.pop()
},
onObjectProperty(property, offset, length, startLine, startCharacter) {
path[path.length - 1] = property
},
onLiteralValue(value, offset, length, startLine, startCharacter) {
// ... process value
}
})