writting a simple JSON parser in js
Opened this issue · 0 comments
Akiq2016 commented
// https://www.json.org/json-en.html
const err = () => {
throw new Error('Not valid JSON');
}
/**
* 1. item splitted by comma
* 2. item type are 'string/array/object' and others
* I. first type, need to find out the right startIndex and endIndex
* II. second type, basic type without complex string
* @param {string} data
*/
const _parseArray = (data) => {
const stack = [];
const comma = ',';
const token = {
'{': '}',
'[': ']',
'"': '"',
}
const itemString = data.slice(1, -1);
let startIndex = 0;
if (itemString.length < 1) return [];
const res = [];
console.log('itemString:', itemString);
for (let i = 0, l = itemString.length; i <= l; i++) {
let checkStack = true;
if (stack[stack.length - 1] === '\\') { // handle \" in string
stack.pop();
if (itemString[i] === `"`) {
checkStack = false;
}
} else if (itemString[i] === '\\') {
stack.push('\\');
checkStack = false;
} else if (stack[stack.length - 1] === '"') { // handle the special token in string "xxx"
if (itemString[i] !== `"`) {
checkStack = false;
}
}
// use stack to record special token pair
if (checkStack) {
if (itemString[i] === stack[stack.length - 1]) {
stack.pop()
console.log('-', token[itemString[i]]);
} else if (token[itemString[i]]) {
stack.push(token[itemString[i]])
console.log('++', itemString[i], i, stack);
}
}
if (!stack.length && itemString[i] === comma || i === itemString.length) {
console.log('==', i, itemString.length, itemString[i], itemString.slice(startIndex, i))
res.push(parseJSON(itemString.slice(startIndex, i)));
startIndex = i + 1;
}
}
return res;
}
/**
* key: string
* value: any type
* @param {string} data
*/
const _parseObject = (data) => {
const stack = [];
let key = null;
let startIndex = 0;
const colon = ':';
const comma = ','
const keyToken = '"';
const token = {
'{': '}',
'[': ']',
'"': '"',
}
const itemString = data.slice(1, -1);
const res = {};
for(let i = 0, l = itemString.length; i <= l; i++) {
// <string:>
if (key === null) {
if (stack.length === 0 && itemString[i] === keyToken) {
stack.push(keyToken);
startIndex = i;
} else if (stack[stack.length - 1] === '\\') {
stack.pop();
} else if (itemString[i] === '\\') {
stack.push('\\');
} else if (itemString[i] === keyToken) {
stack.pop();
}
if (stack.length === 0 && itemString[i] === colon) {
key = parseJSON(itemString.slice(startIndex, i));
startIndex = i + 1;
console.log('key:', key);
}
} else {
// this condition mostly reuse the parseArray's code
let checkStack = true;
if (stack[stack.length - 1] === '\\') {
stack.pop();
if (itemString[i] === `"`) {
checkStack = false;
}
} else if (itemString[i] === '\\') {
stack.push('\\');
checkStack = false;
} else if (stack[stack.length - 1] === '"') {
if (itemString[i] !== `"`) {
checkStack = false;
}
}
if (checkStack) {
if (itemString[i] === stack[stack.length - 1]) {
stack.pop()
} else if (token[itemString[i]]) {
stack.push(token[itemString[i]])
}
}
if (!stack.length && itemString[i] === comma || i === itemString.length) {
console.log('value:', itemString.slice(startIndex, i));
res[key] = parseJSON(itemString.slice(startIndex, i))
startIndex = i + 1;
key = null;
console.log('res:', res);
console.log('========')
}
}
}
return res;
}
/**
*
* @param {string} str
*/
function parseJSON(str) {
if (str.startsWith('"')) { // string
return str.slice(1, -1);
} else if (str === 'true') { // true
return true;
} else if (str === 'false') { // false
return false;
} else if (str === 'null') { // null
return null;
} else if (!Number.isNaN(+str)) { // number
return +str;
} else if (str.startsWith('[')) { // array
return _parseArray(str);
} else if (str.startsWith('{')) { // object
return _parseObject(str);
} else {
err();
}
}
// console.log(parseJSON(JSON.stringify([null,2,true,[4],5])))
// console.log(parseJSON(JSON.stringify([4, 8, ["1[\"", 3, 4]])))
console.log(parseJSON(JSON.stringify({ 'a\,\"': [1, [1]], b: 2 })))