js栈思想实现解析json字符串功能

给你一个json字符串,在不使用 evalJSON.parsenew Function的情况下自己写函数解析成 json对象,你会怎么实现呢?
json的一个特点是数组与对象之间可以相互嵌套,并且无限层级的嵌套,这也是解析json最麻烦的地方。
1、思路:栈思想 “栈”的特性:先进后出(栈只有一个入口和出口,入口就是出口)
现实生活中的物体描述栈: 乒乓球桶,它只有一端可以打开,另一端是封闭的,最先放进去的乒乓球最后才能拿的出来
数据结构: 在JavaScript中实现“栈”的最好数据结构是数组
思路:
  • 1、定义两个栈,栈1(stack1)用例存储json对象,栈2(stack2)用来存储json对象的key和value;定义一个变量用来存储双引号的数量dabbleQuotCount;定义一个变量保存截取的字符串
  • 2、循环整个json字符串,然后一个一个字符的截取json字符串的第一个字符
    • 判断第一个字符是否以{[ 开头,如果是以它们开头,则表示遇到了一个对象或数组,此时需要往栈1中添加一个空对象或空数组,往栈2中添加一个空数组
    • 判断第一个字符是否以双引号(")开头,判断双引号的数量是否为2,为2就说明前面截取的字符串要么是对象的key,要么是对象的value,或者是数组的value;如果不是则继续循环
    • 判断第一个字符是否以逗号(,)开头,如果是则表示前面截取的字符串为对象的值或数组的项
    • 判断第一个字符是否以}] 开头,如果是则表示一个对象或数组结束了
    • 以其他字符串开头,啥都不做,继续循环
1、代码实现
function jsonParser(jsonString){ let surplusStr = jsonString; // 剩余部分 let firstChar = surplusStr.charAt(0); let lastChar = surplusStr.charAt(surplusStr.length - 1); if((firstChar !== '{' && lastChar !== '}') && (firstChar !== '[' && lastChar !== ']')){ throw new Error(`${jsonString}不是一个标准的json对象`); }let jsonObjStack = []; // json对象栈 let keyValuesStack = []; // 存储json对象的键和值的栈(二维数组),它里面的值还是一个数组,该数组中存放对象,对象里保存着键和值 let resultJson = null; let dabbleQuotCount = 0; // 双引号数量 let currentStr = ''; // 当前截取的字符 // 截取字符串 let substr = function (needStoreStr, splitFrom, splitLength){ if(needStoreStr.length > 0){ currentStr += needStoreStr; } surplusStr = surplusStr.substr(splitFrom, splitLength); } // 去除字符串两端双引号 let trimQuot = function (str) { return str.replace(/(^"?)|("?$)/g, ''); } // 存储value到栈顶 let storeValueToStackTop = function (keyValuesStackTop, jsonObjStackTop, value) { if(Array.isArray(jsonObjStackTop)){ keyValuesStackTop.push(value); console.log('进栈', keyValuesStackTop, value); } else { // 获取栈顶的最后一项 let stackTopLast = keyValuesStackTop[keyValuesStackTop.length - 1]; stackTopLast.value = https://www.it610.com/article/value; console.log('进栈', stackTopLast, value); } } // 处理currentStr,转换基本数据类型 let handleCurrentStr = function (str) { if(!isNaN(Number(str))){ str = Number(str); }else if(str == 'null'){ str = null; }else if(str == 'undefined'){ str = undefined; }else if(str == 'true' || str == 'false'){ str = str == 'true'; } return str; } // json对象栈、keyValuesStack栈最后一位出栈 let stackPop = function () { let jsonObjStackTop = jsonObjStack.pop(); let keyValuesStackTop = keyValuesStack.pop(); console.log('出栈', keyValuesStackTop); if(typeof jsonObjStackTop === 'undefined' || typeof keyValuesStackTop === 'undefined') { return; } if(Array.isArray(jsonObjStackTop)){ keyValuesStackTop.forEach(item => { jsonObjStackTop.push(item); }); }else { keyValuesStackTop.forEach(item => { jsonObjStackTop[item.key] = item.value; }); } resultJson = jsonObjStackTop; }while (surplusStr.length > 0) { let firstChar = surplusStr.charAt(0); if(firstChar === '{' || firstChar === '['){ // 第一个字符为“{”则表示遇到一个对象,此时需要添加一个空对象到jsonObjStack中,并且添加一个存储键值的空数组到keyValues中 if(firstChar === '{'){ console.log('首字符为开始大括号'); }else { console.log('首字符为开始中括号'); }let keyValuesStackTop = keyValuesStack[keyValuesStack.length - 1]; let jsonObjStackTop = jsonObjStack[jsonObjStack.length - 1]; let obj = firstChar === '{' ? {} : []; // 每次遇到一个对象就需要往jsonObjStack中添加一个新对象,并且视情况往keyValuesStack栈中添加一个空数组,否则往其父对象中添加 if(typeof jsonObjStackTop !== 'undefined') { if (Array.isArray(jsonObjStackTop)) { keyValuesStackTop.push(obj); } else { let lastKeyValueObj = keyValuesStackTop[keyValuesStackTop.length - 1]; if (typeof lastKeyValueObj.value =https://www.it610.com/article/=='undefined') { lastKeyValueObj.value = https://www.it610.com/article/obj; } } }jsonObjStack.push(obj); keyValuesStack.push([]); surplusStr = surplusStr.substr(1); } else if(firstChar ==='"'){ // 判断为双引号的情况 console.log('首字符为双引号'); // 获取当前截取的字符的最后一个字符 let currentStrLast = typeof currentStr !== 'string' ? '' : currentStr.charAt(currentStr.length - 1); if(currentStrLast !== '\\'){ // 如果当前截取的字符最后面一个字符不是“\”则说明不是转义双引号,即不是"ab\"cd"这种情况 console.log('首字符为双引号,不是“\\”'); dabbleQuotCount++; if(dabbleQuotCount < 2){ console.log('首字符为双引号,未凑成一对双引号'); substr(firstChar, 1); } else { // 如果双引号的数量为2,有可能是对象的key,或是对象的value,或是数组的value substr(firstChar, 1); dabbleQuotCount = 0; // 获取紧挨着第一个字符后面的相邻字符 let nextStr = surplusStr.charAt(0); // 获取栈顶存储的json对象键值 let keyValuesStackTop = keyValuesStack[keyValuesStack.length - 1]; console.log('首字符为双引号,凑成一对双引号,值为:', currentStr, keyValuesStackTop); if(nextStr === ':'){ // 如果nextStr为冒号,则currentStr为对象的key console.log('首字符为双引号,凑成一对双引号,下一个字符为冒号'); keyValuesStackTop.push({ key: trimQuot(currentStr) }); currentStr = ''; substr('', 1); }else if(nextStr === ',' || nextStr === ']' || nextStr === '}'){ // 如果nextStr为逗号或]或},则currentStr为对象或数组的值 let jsonObjStackTop = jsonObjStack[jsonObjStack.length - 1]; console.log('首字符为双引号,凑成一对双引号,下一个字符为:', nextStr, keyValuesStackTop); storeValueToStackTop(keyValuesStackTop, jsonObjStackTop, trimQuot(currentStr)); if(nextStr === ','){ substr('', 1); } currentStr = ''; } } } else { console.log('首字符为双引号,是“\\”'); substr(firstChar, 1); } } else if(firstChar === '}' || firstChar === ']') { // 遇到}结束花括号则表示一个对象结束了 if(firstChar === '{'){ console.log('首字符为结束大括号'); }else { console.log('首字符为结束中括号'); } if(currentStr){ if(firstChar === '{'){ console.log('首字符为结束大括号,并且currentStr有值,值为:', currentStr); }else { console.log('首字符为结束中括号,并且currentStr有值,值为:', currentStr); }let jsonObjStackTop = jsonObjStack[jsonObjStack.length - 1]; let keyValuesStackTop = keyValuesStack[keyValuesStack.length - 1]; let tempVal = handleCurrentStr(currentStr); storeValueToStackTop(keyValuesStackTop, jsonObjStackTop, tempVal); currentStr = ''; } stackPop(); substr('', 1); } else if(firstChar === ',') { // 遇到逗号则表示前面截取的字符串为对象的值或数组的项 console.log('首字符为逗号'); if(dabbleQuotCount == 0){ // 如果双引号的数量为0,则说明逗号未被双引号包着,之前截取的字符串为对象的value或数组项 console.log('首字符为逗号,逗号不在双引号内,值为:', currentStr); if(currentStr){ let keyValuesStackTop = keyValuesStack[keyValuesStack.length - 1]; let jsonObjStackTop = jsonObjStack[jsonObjStack.length - 1]; // console.log('------------keyValuesStackTop', keyValuesStack); let tempVal = handleCurrentStr(currentStr); storeValueToStackTop(keyValuesStackTop, jsonObjStackTop, tempVal); } currentStr = ''; substr('', 1); } else { console.log('首字符为逗号,逗号在双引号内'); substr(firstChar, 1); } } else { console.log('首字符为其他'); substr(firstChar, 1); } }console.log('resultJson', resultJson); if(jsonObjStack.length > 0 || keyValuesStack.length > 0){ throw new Error('json解析失败!'); } return resultJson; }

3、测试一下 【js栈思想实现解析json字符串功能】经测试,以下字符串均可正确解析出来!
let jsonStrSimple = '{"name":"张三","age":23,"man":true,"cleanliness":null}'; let jsonStrWithObj = '{"name":"张三","age":23,"man":true,"cleanliness":null,"score":{"语文":80,"数学":95}}'; let jsonStrWithObj2 = '{"name":"张三","age":23,"man":true,"cleanliness":null,"subject":{"语文":{"teacher":"李老师","score":80},"数学":{"teacher":"王老师","score":95}}}'; let jsonStr = '{"statusCode":200,"comments":"成功","data":{"adminUserId":"61973a868fef766ab4ba953b","roleId":"61973a868fef766ab4ba953c","roleName":null,"orgId":"61973a878fef766ab4ba9686","orgName":"liyn","username":"liyn","email":null,"cellphone":null,"name":null,"nickname":"超级管理员","idcard":null,"adminType":{"code":1,"displayName":"永久账户","name":"PERMANENT"},"adminStatus":{"code":1,"displayName":"已激活","name":"AVAILABLE"},"adminStatusTime":"2021-11-19 13:47:51","loginMode":null,"permitLoginTime":null,"permitLoginIp":null,"online":true,"updatedTime":"2022-01-05 17:41:16","updatedBy":"5da7d124ce5b3053a8e9838d","createdTime":"2021-11-19 13:47:50","createdBy":"5da7d124ce5b3053a8e9838d","adminTypeTime":null,"defaultFlag":false,"loginFailtures":0,"authType":{"code":1,"displayName":"默认策略","name":"DEFAULT"},"sex":null,"permitLoginStart":null,"permitLoginEnd":null,"defaultPassword":false,"delFlag":false,"subject":null,"credible":false,"companyId":"61973a868fef766ab4ba953a","domain":"liyn","logoUrl":"","shortName":"liyn","emailDomain":"","roleVo":{"roleId":"61973a868fef766ab4ba953c","roleName":"机构超级管理员","roleStatus":{"code":1,"displayName":"启用","name":"AVAILABLE"},"roleType":{"code":4,"displayName":"超级模块","name":"SUPER"},"roleCategory":null,"permission":null,"remark":"默认的机构超级管理员","createdBy":"61973a868fef766ab4ba953b","createdTime":null,"updatedBy":null,"updatedTime":"2021-11-19 13:47","defaultFlag":true,"delFlag":false,"credible":false,"menuIds":null}}}'; let jsonStrWithArr = '{"name":"张三","age":23,"man":true,"cleanliness":null,"hobby":["学习","看电影","打球"]}'; let jsonStrWithArr2 = '{"name":"张三","age":23,"man":true,"cleanliness":null,"score":{"语文":80,"数学":95},"hobby":["学习","看电影","打球"]}'; let jsonStrWithArr3 = '{"name":"张三","age":23,"hobby":["学习",2022,"看电影",true,"打球",null,{"hobbyA":123,"arr":["手工","解析","json",456,["嵌套的数组"],undefined,true]}]}'; let arrJson = '["手工","解析","json"]'; let arrJson1 = '["学习",2022,"看电影",true,"打球",null,{"hobbyA":123}]'; let arrJson2 = '["学习",2022,"看电影",true,"打球",null,{"hobbyA":123,"arr":["手工","解析","json",456,["嵌套的数组"],undefined,true]}]'; // jsonParser(jsonStrSimple); // jsonParser(jsonStrWithObj); // jsonParser(jsonStrWithObj2); jsonParser(jsonStr); // jsonParser(jsonStrWithArr); // jsonParser(jsonStrWithArr2); // jsonParser(jsonStrWithArr3); // jsonParser(arrJson); // jsonParser(arrJson1); // jsonParser(arrJson2);

    推荐阅读