Repair button is now capable of turning MongoDB documents into valid JSON
This commit is contained in:
parent
cec16b9de1
commit
3f182a1f04
|
@ -6,6 +6,7 @@ https://github.com/josdejong/jsoneditor
|
||||||
|
|
||||||
- Implemented new mode `preview`, capable of working with large JSON documents
|
- Implemented new mode `preview`, capable of working with large JSON documents
|
||||||
up to 500 MiB.
|
up to 500 MiB.
|
||||||
|
- Repair button is now capable of turning MongoDB documents into valid JSON.
|
||||||
- Fixed #730: in `code` mode, there was an initial undo action which clears
|
- Fixed #730: in `code` mode, there was an initial undo action which clears
|
||||||
the content.
|
the content.
|
||||||
- Upgraded dependencies `vanilla-picker@2.9.1`, `mobius1-selectr@2.4.13`,
|
- Upgraded dependencies `vanilla-picker@2.9.1`, `mobius1-selectr@2.4.13`,
|
||||||
|
|
109
src/js/util.js
109
src/js/util.js
|
@ -36,6 +36,8 @@ exports.parse = function parse(jsonString) {
|
||||||
* @returns {string} json
|
* @returns {string} json
|
||||||
*/
|
*/
|
||||||
exports.repair = function (jsString) {
|
exports.repair = function (jsString) {
|
||||||
|
// TODO: refactor this function, it's too large and complicated now
|
||||||
|
|
||||||
// escape all single and double quotes inside strings
|
// escape all single and double quotes inside strings
|
||||||
var chars = [];
|
var chars = [];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
@ -116,41 +118,49 @@ exports.repair = function (jsString) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse single or double quoted string
|
/**
|
||||||
|
* parse single or double quoted string. Returns the parsed string
|
||||||
|
* @param {string} endQuote
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
function parseString(endQuote) {
|
function parseString(endQuote) {
|
||||||
chars.push('"');
|
var string = '';
|
||||||
|
|
||||||
|
string += '"';
|
||||||
i++;
|
i++;
|
||||||
var c = curr();
|
var c = curr();
|
||||||
while (i < jsString.length && c !== endQuote) {
|
while (i < jsString.length && c !== endQuote) {
|
||||||
if (c === '"' && prev() !== '\\') {
|
if (c === '"' && prev() !== '\\') {
|
||||||
// unescaped double quote, escape it
|
// unescaped double quote, escape it
|
||||||
chars.push('\\"');
|
string += '\\"';
|
||||||
}
|
}
|
||||||
else if (controlChars.hasOwnProperty(c)) {
|
else if (controlChars.hasOwnProperty(c)) {
|
||||||
// replace unescaped control characters with escaped ones
|
// replace unescaped control characters with escaped ones
|
||||||
chars.push(controlChars[c])
|
string += controlChars[c]
|
||||||
}
|
}
|
||||||
else if (c === '\\') {
|
else if (c === '\\') {
|
||||||
// remove the escape character when followed by a single quote ', not needed
|
// remove the escape character when followed by a single quote ', not needed
|
||||||
i++;
|
i++;
|
||||||
c = curr();
|
c = curr();
|
||||||
if (c !== '\'') {
|
if (c !== '\'') {
|
||||||
chars.push('\\');
|
string += '\\';
|
||||||
}
|
}
|
||||||
chars.push(c);
|
string += c;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// regular character
|
// regular character
|
||||||
chars.push(c);
|
string += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
c = curr();
|
c = curr();
|
||||||
}
|
}
|
||||||
if (c === endQuote) {
|
if (c === endQuote) {
|
||||||
chars.push('"');
|
string += '"';
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse an unquoted key
|
// parse an unquoted key
|
||||||
|
@ -167,13 +177,69 @@ exports.repair = function (jsString) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (specialValues.indexOf(key) === -1) {
|
if (specialValues.indexOf(key) === -1) {
|
||||||
chars.push('"' + key + '"');
|
return '"' + key + '"';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
chars.push(key);
|
return key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseMongoDataType () {
|
||||||
|
var c = curr();
|
||||||
|
var value;
|
||||||
|
var dataType = '';
|
||||||
|
while (/[a-zA-Z_$]/.test(c)) {
|
||||||
|
dataType += c
|
||||||
|
i++;
|
||||||
|
c = curr();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType.length > 0 && c === '(') {
|
||||||
|
// This is an MongoDB data type like {"_id": ObjectId("123")}
|
||||||
|
i++;
|
||||||
|
c = curr();
|
||||||
|
if (c === '"') {
|
||||||
|
// a data type containing a string, like ISODate("2012-12-19T06:01:17.171Z")
|
||||||
|
value = parseString(c);
|
||||||
|
c = curr();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// a data type containing a value, like 'NumberLong(2)'
|
||||||
|
value = '';
|
||||||
|
while(c !== ')' && c !== '') {
|
||||||
|
value += c;
|
||||||
|
i++;
|
||||||
|
c = curr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c === ')') {
|
||||||
|
// skip the closing bracket at the end
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// return the value (strip the data type object)
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// huh? that's unexpected. don't touch it
|
||||||
|
return dataType + '(' + value + c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// hm, no Mongo data type after all
|
||||||
|
return dataType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSpecialWhiteSpace (c) {
|
||||||
|
return (
|
||||||
|
c === '\u00A0' ||
|
||||||
|
(c >= '\u2000' && c <= '\u200A') ||
|
||||||
|
c === '\u202F' ||
|
||||||
|
c === '\u205F' ||
|
||||||
|
c === '\u3000')
|
||||||
|
}
|
||||||
|
|
||||||
while(i < jsString.length) {
|
while(i < jsString.length) {
|
||||||
var c = curr();
|
var c = curr();
|
||||||
|
|
||||||
|
@ -183,25 +249,25 @@ exports.repair = function (jsString) {
|
||||||
else if (c === '/' && next() === '/') {
|
else if (c === '/' && next() === '/') {
|
||||||
skipComment();
|
skipComment();
|
||||||
}
|
}
|
||||||
else if (c === '\u00A0' || (c >= '\u2000' && c <= '\u200A') || c === '\u202F' || c === '\u205F' || c === '\u3000') {
|
else if (isSpecialWhiteSpace(c)) {
|
||||||
// special white spaces (like non breaking space)
|
// special white spaces (like non breaking space)
|
||||||
chars.push(' ');
|
chars.push(' ');
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
else if (c === quote) {
|
else if (c === quote) {
|
||||||
parseString(quote);
|
chars.push(parseString(c));
|
||||||
}
|
}
|
||||||
else if (c === quoteDbl) {
|
else if (c === quoteDbl) {
|
||||||
parseString(quoteDbl);
|
chars.push(parseString(quoteDbl));
|
||||||
}
|
}
|
||||||
else if (c === graveAccent) {
|
else if (c === graveAccent) {
|
||||||
parseString(acuteAccent);
|
chars.push(parseString(acuteAccent));
|
||||||
}
|
}
|
||||||
else if (c === quoteLeft) {
|
else if (c === quoteLeft) {
|
||||||
parseString(quoteRight);
|
chars.push(parseString(quoteRight));
|
||||||
}
|
}
|
||||||
else if (c === quoteDblLeft) {
|
else if (c === quoteDblLeft) {
|
||||||
parseString(quoteDblRight);
|
chars.push(parseString(quoteDblRight));
|
||||||
}
|
}
|
||||||
else if (c === ',' && [']', '}'].indexOf(nextNonWhiteSpace()) !== -1) {
|
else if (c === ',' && [']', '}'].indexOf(nextNonWhiteSpace()) !== -1) {
|
||||||
// skip trailing commas
|
// skip trailing commas
|
||||||
|
@ -209,11 +275,16 @@ exports.repair = function (jsString) {
|
||||||
}
|
}
|
||||||
else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) {
|
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();
|
chars.push(parseKey());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
chars.push(c);
|
if (/[a-zA-Z_$]/.test(c)) {
|
||||||
i++;
|
chars.push(parseMongoDataType());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chars.push(c);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,6 @@ describe('util', function () {
|
||||||
assert.strictEqual(util.repair('\n/* foo\nbar */\ncallback_123 ({});\n\n'), '{}');
|
assert.strictEqual(util.repair('\n/* foo\nbar */\ncallback_123 ({});\n\n'), '{}');
|
||||||
|
|
||||||
// non-matching
|
// non-matching
|
||||||
assert.strictEqual(util.repair('callback abc({});'), 'callback abc({});');
|
|
||||||
assert.strictEqual(util.repair('callback {}'), 'callback {}');
|
assert.strictEqual(util.repair('callback {}'), 'callback {}');
|
||||||
assert.strictEqual(util.repair('callback({}'), 'callback({}');
|
assert.strictEqual(util.repair('callback({}'), 'callback({}');
|
||||||
});
|
});
|
||||||
|
@ -93,6 +92,33 @@ describe('util', function () {
|
||||||
assert.strictEqual(util.repair('"{a:2,}"'), '"{a:2,}"');
|
assert.strictEqual(util.repair('"{a:2,}"'), '"{a:2,}"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should strip MongoDB data types', function () {
|
||||||
|
const mongoDocument = '{\n' +
|
||||||
|
' "_id" : ObjectId("123"),\n' +
|
||||||
|
' "isoDate" : ISODate("2012-12-19T06:01:17.171Z"),\n' +
|
||||||
|
' "regularNumber" : 67,\n' +
|
||||||
|
' "long" : NumberLong("2"),\n' +
|
||||||
|
' "long2" : NumberLong(2),\n' +
|
||||||
|
' "int" : NumberInt("3"),\n' +
|
||||||
|
' "int2" : NumberInt(3),\n' +
|
||||||
|
' "decimal" : NumberDecimal("4"),\n' +
|
||||||
|
' "decimal2" : NumberDecimal(4)\n' +
|
||||||
|
'}';
|
||||||
|
|
||||||
|
const expectedJson = '{\n' +
|
||||||
|
' "_id" : "123",\n' +
|
||||||
|
' "isoDate" : "2012-12-19T06:01:17.171Z",\n' +
|
||||||
|
' "regularNumber" : 67,\n' +
|
||||||
|
' "long" : "2",\n' +
|
||||||
|
' "long2" : 2,\n' +
|
||||||
|
' "int" : "3",\n' +
|
||||||
|
' "int2" : 3,\n' +
|
||||||
|
' "decimal" : "4",\n' +
|
||||||
|
' "decimal2" : 4\n' +
|
||||||
|
'}';
|
||||||
|
|
||||||
|
assert.strictEqual(util.repair(mongoDocument), expectedJson);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('jsonPath', function () {
|
describe('jsonPath', function () {
|
||||||
|
|
Loading…
Reference in New Issue