Improvements in sanitizing invalid JSON

This commit is contained in:
jos 2016-01-16 14:49:04 +01:00
parent 5a5c04e48f
commit 8ec471b6dc
3 changed files with 33 additions and 16 deletions

View File

@ -3,6 +3,11 @@
https://github.com/josdejong/jsoneditor https://github.com/josdejong/jsoneditor
## not yet released, version 5.1.2
- Improvements in sanitizing invalid JSON.
## 2016-01-16, version 5.1.1 ## 2016-01-16, version 5.1.1
- Fixed #257: Improving error messages for enum errors failed when the - Fixed #257: Improving error messages for enum errors failed when the

View File

@ -45,26 +45,23 @@ exports.sanitize = function (jsString) {
function next() { return jsString.charAt(i + 1); } function next() { return jsString.charAt(i + 1); }
function prev() { return jsString.charAt(i - 1); } function prev() { return jsString.charAt(i - 1); }
// test whether the last non-whitespace character was a brace-open '{' // get the last parsed non-whitespace character
function prevIsBrace() { function lastNonWhitespace () {
var ii = i - 1; var p = chars.length - 1;
while (ii >= 0) {
var cc = jsString.charAt(ii); while (p >= 0) {
if (cc === '{') { var pp = chars[p];
return true; if (pp !== ' ' && pp !== '\n' && pp !== '\r' && pp !== '\t') { // non whitespace
return pp;
} }
else if (cc === ' ' || cc === '\n' || cc === '\r') { // whitespace p--;
ii--;
} }
else {
return false; return '';
}
}
return false;
} }
// skip a block comment '/* ... */' // skip a block comment '/* ... */'
function skipComment () { function skipBlockComment () {
i += 2; i += 2;
while (i < jsString.length && (curr() !== '*' || next() !== '/')) { while (i < jsString.length && (curr() !== '*' || next() !== '/')) {
i++; i++;
@ -72,6 +69,14 @@ exports.sanitize = function (jsString) {
i += 2; i += 2;
} }
// skip a comment '// ...'
function skipComment () {
i += 2;
while (i < jsString.length && (curr() !== '\n')) {
i++;
}
}
// parse single or double quoted string // parse single or double quoted string
function parseString(quote) { function parseString(quote) {
chars.push('"'); chars.push('"');
@ -129,12 +134,15 @@ exports.sanitize = function (jsString) {
var c = curr(); var c = curr();
if (c === '/' && next() === '*') { if (c === '/' && next() === '*') {
skipBlockComment();
}
else if (c === '/' && next() === '/') {
skipComment(); skipComment();
} }
else if (c === '\'' || c === '"') { else if (c === '\'' || c === '"') {
parseString(c); parseString(c);
} }
else if (/[a-zA-Z_$]/.test(c) && prevIsBrace()) { else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) {
// an unquoted object key (like a in '{a:2}') // an unquoted object key (like a in '{a:2}')
parseKey(); parseKey();
} }

View File

@ -13,6 +13,7 @@ describe('util', function () {
assert.equal(util.sanitize('{a:2}'), '{"a":2}'); assert.equal(util.sanitize('{a:2}'), '{"a":2}');
assert.equal(util.sanitize('{\'a\':2}'), '{"a":2}'); assert.equal(util.sanitize('{\'a\':2}'), '{"a":2}');
assert.equal(util.sanitize('{a:\'foo\'}'), '{"a":"foo"}'); assert.equal(util.sanitize('{a:\'foo\'}'), '{"a":"foo"}');
assert.equal(util.sanitize('{a:\'foo\',b:\'bar\'}'), '{"a":"foo","b":"bar"}');
// should leave string content untouched // should leave string content untouched
assert.equal(util.sanitize('"{a:b}"'), '"{a:b}"'); assert.equal(util.sanitize('"{a:b}"'), '"{a:b}"');
@ -28,6 +29,9 @@ describe('util', function () {
it('remove comments', function () { it('remove comments', function () {
assert.equal(util.sanitize('/* foo */ {}'), ' {}'); assert.equal(util.sanitize('/* foo */ {}'), ' {}');
assert.equal(util.sanitize('/* foo */ {}'), ' {}');
assert.equal(util.sanitize('{a:\'foo\',/*hello*/b:\'bar\'}'), '{"a":"foo","b":"bar"}');
assert.equal(util.sanitize('{\na:\'foo\',//hello\nb:\'bar\'\n}'), '{\n"a":"foo",\n"b":"bar"\n}');
// should not remove comments in string // should not remove comments in string
assert.equal(util.sanitize('{"str":"/* foo */"}'), '{"str":"/* foo */"}'); assert.equal(util.sanitize('{"str":"/* foo */"}'), '{"str":"/* foo */"}');