Fixed #676: JSON Paths containing array properties with a `]` not parsed correctly

This commit is contained in:
jos 2019-03-20 17:34:39 +01:00
parent 7c398aeef7
commit 3e7e1cebfd
4 changed files with 80 additions and 36 deletions

View File

@ -9,6 +9,8 @@ https://github.com/josdejong/jsoneditor
styling can be applied for default and non-default values. Thanks @AdamVig. styling can be applied for default and non-default values. Thanks @AdamVig.
- Fixed #667: resolving JSON Schema examples and descriptions did not always - Fixed #667: resolving JSON Schema examples and descriptions did not always
work for referenced schemas. Thanks @AdamVig. work for referenced schemas. Thanks @AdamVig.
- Fixed #676: JSON Paths containing array properties with a `]` not parsed
correctly.
## 2019-03-14, version 5.31.1 ## 2019-03-14, version 5.31.1

View File

@ -43,3 +43,10 @@ if (!Array.prototype.find) {
} }
} }
} }
// Polyfill for String.trim
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
}

View File

@ -742,43 +742,75 @@ exports.isChildOf = function (elem, parent) {
* @return {Array} * @return {Array}
*/ */
exports.parsePath = function parsePath(jsonPath) { exports.parsePath = function parsePath(jsonPath) {
var prop, remainder; var path = [];
var i = 0;
if (jsonPath.length === 0) { function parseProperty () {
return []; var prop = ''
while (jsonPath[i] !== undefined && /[\w$]/.test(jsonPath[i])) {
prop += jsonPath[i];
i++;
} }
// find a match like '.prop' if (prop === '') {
var match = jsonPath.match(/^\.([\w$]+)/); throw new Error('Invalid JSON path: property name expected at index ' + i);
if (match) {
prop = match[1];
remainder = jsonPath.substr(prop.length + 1);
}
else if (jsonPath[0] === '[') {
// find a match like
var end = jsonPath.indexOf(']');
if (end === -1) {
throw new SyntaxError('Character ] expected in path');
}
if (end === 1) {
throw new SyntaxError('Index expected after [');
} }
var value = jsonPath.substring(1, end); return prop;
if (value[0] === '\'') {
// ajv produces string prop names with single quotes, so we need
// to reformat them into valid double-quoted JSON strings
value = '\"' + value.substring(1, value.length - 1) + '\"';
} }
prop = value === '*' ? value : JSON.parse(value); // parse string and number function parseIndex (end) {
remainder = jsonPath.substr(end + 1); var name = ''
while (jsonPath[i] !== undefined && jsonPath[i] !== end) {
name += jsonPath[i];
i++;
}
if (jsonPath[i] !== end) {
throw new Error('Invalid JSON path: unexpected end, character ' + end + ' expected')
}
return name;
}
while (jsonPath[i] !== undefined) {
if (jsonPath[i] === '.') {
i++;
path.push(parseProperty());
}
else if (i > 0 && jsonPath[i] === '[') {
i++;
if (jsonPath[i] === '\'' || jsonPath[i] === '"') {
var end = jsonPath[i]
i++;
path.push(parseIndex(end));
if (jsonPath[i] !== end) {
throw new Error('Invalid JSON path: closing quote \' expected at index ' + i)
}
i++;
} }
else { else {
throw new SyntaxError('Failed to parse path'); var index = parseIndex(']').trim()
if (index.length === 0) {
throw new Error('Invalid JSON path: array value expected at index ' + i)
}
path.push(index);
} }
return [prop].concat(parsePath(remainder)) if (jsonPath[i] !== ']') {
throw new Error('Invalid JSON path: closing bracket ] expected at index ' + i)
}
i++;
}
else {
throw new Error('Invalid JSON path: unexpected character "' + jsonPath[i] + '" at index ' + i);
}
}
return path;
}; };
/** /**

View File

@ -105,16 +105,19 @@ describe('util', function () {
assert.deepEqual(util.parsePath('.foo[2].bar'), ['foo', 2, 'bar']); assert.deepEqual(util.parsePath('.foo[2].bar'), ['foo', 2, 'bar']);
assert.deepEqual(util.parsePath('.foo["prop with spaces"]'), ['foo', 'prop with spaces']); assert.deepEqual(util.parsePath('.foo["prop with spaces"]'), ['foo', 'prop with spaces']);
assert.deepEqual(util.parsePath('.foo[\'prop with single quotes as outputted by ajv library\']'), ['foo', 'prop with single quotes as outputted by ajv library']); assert.deepEqual(util.parsePath('.foo[\'prop with single quotes as outputted by ajv library\']'), ['foo', 'prop with single quotes as outputted by ajv library']);
assert.deepEqual(util.parsePath('.foo["prop with . dot"]'), ['foo', 'prop with . dot']);
assert.deepEqual(util.parsePath('.foo["prop with ] character"]'), ['foo', 'prop with ] character']);
assert.deepEqual(util.parsePath('.foo[*].bar'), ['foo', '*', 'bar']); assert.deepEqual(util.parsePath('.foo[*].bar'), ['foo', '*', 'bar']);
}); });
it ('should throw an exception in case of an invalid path', function () { it ('should throw an exception in case of an invalid path', function () {
assert.throws(function () {util.parsePath('.')}, /Error/); assert.throws(function () {util.parsePath('.')}, /Invalid JSON path: property name expected at index 1/);
assert.throws(function () {util.parsePath('[')}, /Error/); assert.throws(function () {util.parsePath('[')}, /Invalid JSON path: unexpected character "\[" at index 0/);
assert.throws(function () {util.parsePath('[]')}, /Error/); assert.throws(function () {util.parsePath('[]')}, /Invalid JSON path: unexpected character "\[" at index 0/);
assert.throws(function () {util.parsePath('.[]')}, /Error/); assert.throws(function () {util.parsePath('.foo[ ]')}, /Invalid JSON path: array value expected at index 7/);
assert.throws(function () {util.parsePath('["23]')}, /Error/); assert.throws(function () {util.parsePath('.[]')}, /Invalid JSON path: property name expected at index 1/);
assert.throws(function () {util.parsePath('.foo bar')}, /Error/); assert.throws(function () {util.parsePath('["23]')}, /Invalid JSON path: unexpected character "\[" at index 0/);
assert.throws(function () {util.parsePath('.foo bar')}, /Invalid JSON path: unexpected character " " at index 4/);
}); });
}); });