给你一个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 = 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 === 'undefined') {            lastKeyValueObj.value = 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、测试一下

经测试,以下字符串均可正确解析进去!

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);