big-json-viewer
Version:
JavaScript Library to view big JSON structures.
465 lines • 16.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("../helpers/utils");
var BRACE_START = '{'.charCodeAt(0);
var BRACE_END = '}'.charCodeAt(0);
var BRACKET_START = '['.charCodeAt(0);
var BRACKET_END = ']'.charCodeAt(0);
var COLON = ':'.charCodeAt(0);
var COMMA = ','.charCodeAt(0);
var DOUBLE_QUOTE = '"'.charCodeAt(0);
var SINGLE_QUOTE = "'".charCodeAt(0);
var SPACE = ' '.charCodeAt(0);
var TAB = '\t'.charCodeAt(0);
var NEWLINE = '\n'.charCodeAt(0);
var BACKSPACE = '\b'.charCodeAt(0);
var CARRIAGE_RETURN = '\r'.charCodeAt(0);
var FORM_FEED = '\f'.charCodeAt(0);
var BACK_SLASH = '\\'.charCodeAt(0);
var FORWARD_SLASH = '/'.charCodeAt(0);
var MINUS = '-'.charCodeAt(0);
var PLUS = '+'.charCodeAt(0);
var DOT = '.'.charCodeAt(0);
var CHAR_E_LOW = 'e'.charCodeAt(0);
var CHAR_E_HIGH = 'E'.charCodeAt(0);
var DIGIT_0 = '0'.charCodeAt(0);
var DIGIT_9 = '9'.charCodeAt(0);
var IGNORED = [SPACE, TAB, NEWLINE, CARRIAGE_RETURN];
var NULL = 'null'.split('').map(function (d) { return d.charCodeAt(0); });
var TRUE = 'true'.split('').map(function (d) { return d.charCodeAt(0); });
var FALSE = 'false'.split('').map(function (d) { return d.charCodeAt(0); });
var BufferJsonNodeInfo = /** @class */ (function () {
function BufferJsonNodeInfo(parser, index, path) {
this.path = [];
this.parser = parser;
this.index = index;
this.path = path;
}
/**
* Returns the list of keys in case of an object for the defined range
* @param {number} start
* @param {number} limit
*/
BufferJsonNodeInfo.prototype.getObjectKeys = function (start, limit) {
if (start === void 0) { start = 0; }
if (this.type !== 'object') {
throw new Error("Unsupported method on non-object " + this.type);
}
utils_1.assertStartLimit(start, limit);
var ctx = {
path: this.path,
objectKeys: [],
start: start,
limit: limit
};
this.parser.parseObject(this.index, ctx);
return ctx.objectKeys;
};
/**
* Return the NodeInfo at the defined position.
* Use the index from getObjectKeys
* @param index
*/
BufferJsonNodeInfo.prototype.getByIndex = function (index) {
if (this.type === 'object') {
var nodes = this.getObjectNodes(index, 1);
if (nodes.length) {
return nodes[0];
}
}
if (this.type === 'array') {
var nodes = this.getArrayNodes(index, 1);
if (nodes.length) {
return nodes[0];
}
}
return undefined;
};
/**
* Return the NodeInfo for the specified key
* Use the index from getObjectKeys
* @param key
*/
BufferJsonNodeInfo.prototype.getByKey = function (key) {
if (this.type === 'object') {
var ctx = {
path: this.path,
objectKey: key
};
this.parser.parseObject(this.index, ctx);
return ctx.objectNodes ? ctx.objectNodes[0] : undefined;
}
if (this.type === 'array') {
return this.getByIndex(parseInt(key));
}
return undefined;
};
/**
* Find the information for a given path
* @param {string[]} path
*/
BufferJsonNodeInfo.prototype.getByPath = function (path) {
if (!path) {
return undefined;
}
if (!path.length) {
return this;
}
var p = path.slice();
var key;
var node = this;
while ((key = p.shift()) !== undefined && node) {
node = node.getByKey(key);
}
return node;
};
/**
* Returns a list with the NodeInfo objects for the defined range
* @param {number} start
* @param {number} limit
*/
BufferJsonNodeInfo.prototype.getObjectNodes = function (start, limit) {
if (start === void 0) { start = 0; }
if (this.type !== 'object') {
throw new Error("Unsupported method on non-object " + this.type);
}
utils_1.assertStartLimit(start, limit);
var ctx = {
path: this.path,
objectNodes: [],
start: start,
limit: limit
};
this.parser.parseObject(this.index, ctx);
return ctx.objectNodes;
};
/**
* Returns a list of NodeInfo for the defined range
* @param {number} start
* @param {number} limit
*/
BufferJsonNodeInfo.prototype.getArrayNodes = function (start, limit) {
if (start === void 0) { start = 0; }
if (this.type !== 'array') {
throw new Error("Unsupported method on non-array " + this.type);
}
utils_1.assertStartLimit(start, limit);
var ctx = {
path: this.path,
arrayNodes: [],
start: start,
limit: limit
};
this.parser.parseArray(this.index, ctx);
return ctx.arrayNodes;
};
/**
* Get the natively parsed value
*/
BufferJsonNodeInfo.prototype.getValue = function () {
return this.parser.parseNative(this.index, this.index + this.chars);
};
return BufferJsonNodeInfo;
}());
exports.BufferJsonNodeInfo = BufferJsonNodeInfo;
/**
* Parses meta data about a JSON structure in an ArrayBuffer.
*/
var BufferJsonParser = /** @class */ (function () {
function BufferJsonParser(data) {
if (data instanceof ArrayBuffer) {
this.data = new Uint16Array(data);
}
else if (typeof data === 'string' && typeof TextEncoder !== 'undefined') {
this.data = new TextEncoder().encode(data);
}
else if (typeof data === 'string') {
this.data = new Uint16Array(new ArrayBuffer(data.length * 2));
for (var i = 0; i < data.length; i++) {
this.data[i] = data.charCodeAt(i);
}
}
}
BufferJsonParser.prototype.getRootNodeInfo = function () {
var start = this.skipIgnored(0);
var ctx = {
path: [],
nodeInfo: new BufferJsonNodeInfo(this, start, [])
};
var end = this.parseValue(start, ctx, false);
if (start === end) {
return null;
}
return ctx.nodeInfo;
};
BufferJsonParser.prototype.parseValue = function (start, ctx, throwUnknown) {
if (throwUnknown === void 0) { throwUnknown = true; }
var char = this.data[start];
if (isString(char)) {
return this.parseString(start, ctx);
}
if (isNumber(char)) {
return this.parseNumber(start, ctx);
}
if (char === BRACE_START) {
return this.parseObject(start, ctx);
}
if (char === BRACKET_START) {
return this.parseArray(start, ctx);
}
if (char === TRUE[0]) {
return this.parseToken(start, TRUE, ctx);
}
if (char === FALSE[0]) {
return this.parseToken(start, FALSE, ctx);
}
if (char === NULL[0]) {
return this.parseToken(start, NULL, ctx);
}
if (throwUnknown) {
throw new Error("parse value unknown token " + bufToString(char) + " at " + start);
}
function isString(char) {
return char === DOUBLE_QUOTE || char === SINGLE_QUOTE;
}
function isNumber(char) {
return char === MINUS || (char >= DIGIT_0 && char <= DIGIT_9);
}
};
BufferJsonParser.prototype.parseObject = function (start, ctx) {
var index = start + 1; // skip the start brace
var length = 0;
var keys = [];
var nodes = [];
while (index <= this.data.length) {
if (index === this.data.length) {
throw new Error("parse object incomplete at end");
}
index = this.skipIgnored(index);
if (this.data[index] === BRACE_END) {
index++;
break;
}
var keyCtx = getKeyContext(length);
index = this.parseString(index, keyCtx);
if (keyCtx && ctx && ctx.objectKeys) {
keys.push(keyCtx.value);
}
index = this.skipIgnored(index);
if (this.data[index] !== COLON) {
throw new Error("parse object unexpected token " + bufToString(this.data[index]) + " at " + index + ". Expected :");
}
else {
index++;
}
index = this.skipIgnored(index);
var valueCtx = null;
if (keyCtx &&
ctx &&
(ctx.objectNodes || keyCtx.value === ctx.objectKey)) {
valueCtx = {
path: ctx.path,
nodeInfo: new BufferJsonNodeInfo(this, index, ctx.path.concat([
keyCtx.value
]))
};
}
index = this.parseValue(index, valueCtx);
index = this.skipIgnored(index);
if (valueCtx && ctx.objectNodes) {
nodes.push(valueCtx.nodeInfo);
}
else if (valueCtx && ctx.objectKey !== undefined) {
ctx.objectNodes = [valueCtx.nodeInfo];
return;
}
length++;
if (this.data[index] === COMMA) {
index++;
}
else if (this.data[index] !== BRACE_END) {
throw new Error("parse object unexpected token " + bufToString(this.data[index]) + " at " + index + ". Expected , or }");
}
}
if (ctx && ctx.nodeInfo) {
ctx.nodeInfo.type = 'object';
ctx.nodeInfo.length = length;
ctx.nodeInfo.chars = index - start;
}
if (ctx && ctx.objectKeys) {
ctx.objectKeys = keys;
}
if (ctx && ctx.objectNodes) {
ctx.objectNodes = nodes;
}
function getKeyContext(keyIndex) {
if (!ctx ||
(ctx.start && keyIndex < ctx.start) ||
(ctx.limit && keyIndex >= ctx.start + ctx.limit)) {
return null;
}
if (ctx &&
(ctx.objectKeys || ctx.objectNodes || ctx.objectKey !== undefined)) {
return {
path: ctx.path,
value: null
};
}
return null;
}
return index;
};
BufferJsonParser.prototype.parseArray = function (start, ctx) {
var index = start + 1; // skip the start bracket
var length = 0;
while (index <= this.data.length) {
if (index === this.data.length) {
throw new Error("parse array incomplete at end");
}
index = this.skipIgnored(index);
if (this.data[index] === BRACKET_END) {
index++;
break;
}
var valueCtx = null;
if (isInRange(length) && ctx.arrayNodes) {
valueCtx = {
path: ctx.path,
nodeInfo: new BufferJsonNodeInfo(this, index, ctx.path.concat([
length.toString()
]))
};
}
index = this.parseValue(index, valueCtx);
if (valueCtx) {
ctx.arrayNodes.push(valueCtx.nodeInfo);
}
index = this.skipIgnored(index);
length++;
if (this.data[index] === COMMA) {
index++;
}
else if (this.data[index] !== BRACKET_END) {
throw new Error("parse array unexpected token " + bufToString(this.data[index]) + " at " + index + ". Expected , or ]");
}
}
if (ctx && ctx.nodeInfo) {
ctx.nodeInfo.type = 'array';
ctx.nodeInfo.length = length;
ctx.nodeInfo.chars = index - start;
}
function isInRange(keyIndex) {
return !(!ctx ||
(ctx.start && keyIndex < ctx.start) ||
(ctx.limit && keyIndex >= ctx.start + ctx.limit));
}
return index;
};
BufferJsonParser.prototype.parseString = function (start, ctx) {
var index = start;
var expect = this.data[index] === DOUBLE_QUOTE ? DOUBLE_QUOTE : SINGLE_QUOTE;
var esc = false, length = 0;
for (index++; index <= this.data.length; index++) {
if (index === this.data.length) {
throw new Error("parse string incomplete at end");
}
if (!esc && this.data[index] === expect) {
index++;
break;
}
if (this.data[index] === BACK_SLASH) {
esc = !esc;
}
else {
esc = false;
}
if (!esc) {
length++;
}
}
if (ctx && ctx.nodeInfo) {
ctx.nodeInfo.type = 'string';
ctx.nodeInfo.length = length;
ctx.nodeInfo.chars = index - start;
}
if (ctx && ctx.value !== undefined) {
ctx.value = JSON.parse(bufToString(this.data.subarray(start, index)));
}
return index;
};
BufferJsonParser.prototype.parseNumber = function (start, ctx) {
var i = start;
if (this.data[i] === MINUS) {
i++;
}
i = this.parseDigits(i);
if (this.data[i] === DOT) {
i++;
i = this.parseDigits(i);
}
if (this.data[i] === CHAR_E_HIGH || this.data[i] === CHAR_E_LOW) {
i++;
if (this.data[i] === PLUS || this.data[i] === MINUS) {
i++;
}
i = this.parseDigits(i);
}
if (ctx && ctx.nodeInfo) {
ctx.nodeInfo.type = 'number';
ctx.nodeInfo.chars = i - start;
}
if (ctx && ctx.value !== undefined) {
ctx.value = JSON.parse(bufToString(this.data.subarray(start, i)));
}
return i;
};
BufferJsonParser.prototype.parseDigits = function (start) {
while (this.data[start] >= DIGIT_0 && this.data[start] <= DIGIT_9) {
start++;
}
return start;
};
BufferJsonParser.prototype.parseToken = function (start, chars, ctx) {
var index = start;
for (var i = 0; i < chars.length; i++) {
if (this.data[index] !== chars[i]) {
throw new Error("Unexpected token " + bufToString(this.data[index]) + " at " + index + ". Expected " + bufToString(chars));
}
index++;
}
var token = bufToString(this.data.subarray(start, index));
if (ctx && ctx.nodeInfo) {
if (token === 'null') {
ctx.nodeInfo.type = 'null';
}
else {
ctx.nodeInfo.type = 'boolean';
}
ctx.nodeInfo.chars = index - start;
}
if (ctx && ctx.value !== undefined) {
ctx.value = JSON.parse(token);
}
return index;
};
BufferJsonParser.prototype.parseNative = function (start, end) {
return JSON.parse(bufToString(this.data.subarray(start, end)));
};
BufferJsonParser.prototype.skipIgnored = function (start) {
for (var i = start; i < this.data.length; i++) {
if (IGNORED.indexOf(this.data[i]) !== -1) {
continue;
}
return i;
}
};
return BufferJsonParser;
}());
exports.BufferJsonParser = BufferJsonParser;
function bufToString(buf) {
if (typeof buf === 'number') {
buf = [buf];
}
return String.fromCharCode.apply(null, buf);
}
//# sourceMappingURL=buffer-json-parser.js.map