js stack to realize the function of parsing json string

Give you a json string without using eval and json In the case of parse and new functions, write your own functions and parse them into json objects. How would you implement them?

One of the characteristics of json is that arrays and objects can be nested with each other, and there are infinite levels of nesting, which is also the most troublesome place to parse json.

1. Thought: stack thought

Characteristics of "stack": first in and last out (the stack has only one entrance and exit, and the entrance is the exit)
Object description stack in real life: table tennis bucket. Only one end can be opened and the other end is closed. The table tennis that is put in first can be taken out last
Data structure: the best data structure to implement "stack" in JavaScript is array
Idea:

  • 1. Define two stacks. Stack 1 (stack 1) is used to store json objects, and stack 2 (stack 2) is used to store key s and value s of json objects; Define a variable to store the number of double quotes dabbleQuotCount; Define a variable to hold the intercepted string
  • 2. Loop the entire json string, and then intercept the first character of the json string one character at a time

    • Judge whether the first character starts with {, [or not. If it starts with them, it indicates that an object or array has been encountered. At this time, you need to add an empty object or array to stack 1 and an empty array to stack 2
    • Judge whether the first character starts with double quotation marks ("), and judge whether the number of double quotation marks is 2. If it is 2, it means that the previously intercepted string is either the key of the object, the value of the object, or the value of the array; if not, continue the loop
    • Judge whether the first character begins with a comma (,). If so, it indicates that the previously intercepted string is the value of the object or the item of the array
    • Judge whether the first character starts with},]. If so, it means that an object or array ends
    • Start with another string, do nothing, and continue the loop

1. Code implementation

function jsonParser(jsonString){
  let surplusStr = jsonString; // rest
  let firstChar = surplusStr.charAt(0);
  let lastChar = surplusStr.charAt(surplusStr.length - 1);
  if((firstChar !== '{' && lastChar !== '}') && (firstChar !== '[' && lastChar !== ']')){
    throw new Error(`${jsonString}Not a standard json object`);
  }

  let jsonObjStack = []; // json object stack
  let keyValuesStack = []; // A stack (two-dimensional array) that stores the keys and values of json objects. Its values are still an array. The array stores objects, and the objects store keys and values
  let resultJson = null;

  let dabbleQuotCount = 0; // Number of double quotes
  let currentStr = ''; // Characters currently intercepted
  // Intercept string
  let substr = function (needStoreStr, splitFrom, splitLength){
    if(needStoreStr.length > 0){
      currentStr += needStoreStr;
    }
    surplusStr = surplusStr.substr(splitFrom, splitLength);
  }
  // Remove the double quotation marks at both ends of the string
  let trimQuot = function (str) {
    return str.replace(/(^"?)|("?$)/g, '');
  }
  // Store value to the top of the stack
  let storeValueToStackTop = function (keyValuesStackTop, jsonObjStackTop, value) {
    if(Array.isArray(jsonObjStackTop)){
      keyValuesStackTop.push(value);
      console.log('Enter the stack', keyValuesStackTop, value);
    } else {
      // Gets the last item at the top of the stack
      let stackTopLast = keyValuesStackTop[keyValuesStackTop.length - 1];
      stackTopLast.value = value;
      console.log('Enter the stack', stackTopLast, value);
    }
  }
  // Process currentStr and convert basic data types
  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;
  }
  // The last bit of json object stack and keyValuesStack stack is out of the stack
  let stackPop = function () {
    let jsonObjStackTop = jsonObjStack.pop();
    let keyValuesStackTop = keyValuesStack.pop();
    console.log('Out of stack', 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 === '['){ // The first character "{" indicates that an object is encountered. At this time, you need to add an empty object to jsonObjStack and an empty array storing key values to keyValues
      if(firstChar === '{'){
        console.log('The first character is a starting brace');
      }else {
        console.log('The first character is the opening bracket');
      }

      let keyValuesStackTop = keyValuesStack[keyValuesStack.length - 1];
      let jsonObjStackTop = jsonObjStack[jsonObjStack.length - 1];
      let obj = firstChar === '{' ? {} : [];
      // Each time an object is encountered, you need to add a new object to the jsonObjStack, and add an empty array to the keyValuesStack stack as appropriate, otherwise add it to its parent object
      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 === '"'){ // Judged as double quotation marks
      console.log('The first character is a double quotation mark');
      // Gets the last character of the currently intercepted character
      let currentStrLast = typeof currentStr !== 'string' ? '' : currentStr.charAt(currentStr.length - 1);
      if(currentStrLast !== '\\'){ // If the last character of the currently intercepted character is not "\", it means that it is not an escaped double quotation mark, that is, it is not "ab\"cd "
        console.log('The first character is a double quotation mark, not a“\\"');
        dabbleQuotCount++;
        if(dabbleQuotCount < 2){
          console.log('The first character is a double quotation mark, which is not rounded into a pair of double quotation marks');
          substr(firstChar, 1);
        } else { // If the number of double quotes is 2, it may be the key of the object, the value of the object, or the value of the array
          substr(firstChar, 1);
          dabbleQuotCount = 0;
          // Gets the adjacent character immediately after the first character
          let nextStr = surplusStr.charAt(0);
          // Get the json object key value stored at the top of the stack
          let keyValuesStackTop = keyValuesStack[keyValuesStack.length - 1];
          console.log('The first character is double quotation marks, which form a pair of double quotation marks. The value is:', currentStr, keyValuesStackTop);
          if(nextStr === ':'){ // If nextStr is a colon, currentStr is the key of the object
            console.log('The first character is a pair of double quotation marks, and the next character is a colon');
            keyValuesStackTop.push({
              key: trimQuot(currentStr)
            });
            currentStr = '';
            substr('', 1);
          }else if(nextStr === ',' || nextStr === ']' || nextStr === '}'){ // If nextStr is a comma or] or}, currentStr is the value of the object or array
            let jsonObjStackTop = jsonObjStack[jsonObjStack.length - 1];
            console.log('The first character is a pair of double quotation marks, and the next character is:', nextStr, keyValuesStackTop);
            storeValueToStackTop(keyValuesStackTop, jsonObjStackTop, trimQuot(currentStr));
            if(nextStr === ','){
              substr('', 1);
            }
            currentStr = '';
          }
        }
      } else {
        console.log('The first character is double quotation mark, yes“\\"');
        substr(firstChar, 1);
      }
    } else if(firstChar === '}' || firstChar === ']') { // Encountering} closing curly braces indicates that an object has ended
      if(firstChar === '{'){
        console.log('The first character is the closing brace');
      }else {
        console.log('The first character is the closing bracket');
      }
      if(currentStr){
        if(firstChar === '{'){
          console.log('The first character is a closing brace, and currentStr There are values, which are:', currentStr);
        }else {
          console.log('The first character is the closing bracket, and currentStr There are values, which are:', 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 === ',') { // If a comma is encountered, it means that the previously intercepted string is the value of the object or the item of the array
      console.log('The first character is a comma');
      if(dabbleQuotCount == 0){ // If the number of double quotation marks is 0, the comma is not enclosed by double quotation marks, and the previously intercepted string is the value or array item of the object
        console.log('The first character is a comma, the comma is not in double quotation marks, and the value is:', 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('The first character is a comma, which is in double quotation marks');
        substr(firstChar, 1);
      }
    } else {
      console.log('The first character is other');
      substr(firstChar, 1);
    }
  }

  console.log('resultJson', resultJson);
  if(jsonObjStack.length > 0 || keyValuesStack.length > 0){
    throw new Error('json Parsing failed!');
  }
  return resultJson;
}

3. Test it

After testing, the following strings can be parsed correctly!

let jsonStrSimple = '{"name":"Zhang San","age":23,"man":true,"cleanliness":null}';
let jsonStrWithObj = '{"name":"Zhang San","age":23,"man":true,"cleanliness":null,"score":{"language":80,"mathematics":95}}';
let jsonStrWithObj2 = '{"name":"Zhang San","age":23,"man":true,"cleanliness":null,"subject":{"language":{"teacher":"Miss Li","score":80},"mathematics":{"teacher":"Miss Wang","score":95}}}';
let jsonStr = '{"statusCode":200,"comments":"success","data":{"adminUserId":"61973a868fef766ab4ba953b","roleId":"61973a868fef766ab4ba953c","roleName":null,"orgId":"61973a878fef766ab4ba9686","orgName":"liyn","username":"liyn","email":null,"cellphone":null,"name":null,"nickname":"Super administrator","idcard":null,"adminType":{"code":1,"displayName":"Permanent account","name":"PERMANENT"},"adminStatus":{"code":1,"displayName":"Activated","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":"Default policy","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":"Organization super administrator","roleStatus":{"code":1,"displayName":"Enable","name":"AVAILABLE"},"roleType":{"code":4,"displayName":"Supermodule","name":"SUPER"},"roleCategory":null,"permission":null,"remark":"Default organization super administrator","createdBy":"61973a868fef766ab4ba953b","createdTime":null,"updatedBy":null,"updatedTime":"2021-11-19 13:47","defaultFlag":true,"delFlag":false,"credible":false,"menuIds":null}}}';


let jsonStrWithArr = '{"name":"Zhang San","age":23,"man":true,"cleanliness":null,"hobby":["study","watch movie","play a ball"]}';
let jsonStrWithArr2 = '{"name":"Zhang San","age":23,"man":true,"cleanliness":null,"score":{"language":80,"mathematics":95},"hobby":["study","watch movie","play a ball"]}';
let jsonStrWithArr3 = '{"name":"Zhang San","age":23,"hobby":["study",2022,"watch movie",true,"play a ball",null,{"hobbyA":123,"arr":["manual","analysis","json",456,["Nested array"],undefined,true]}]}';

let arrJson = '["manual","analysis","json"]';
let arrJson1 = '["study",2022,"watch movie",true,"play a ball",null,{"hobbyA":123}]';
let arrJson2 = '["study",2022,"watch movie",true,"play a ball",null,{"hobbyA":123,"arr":["manual","analysis","json",456,["Nested array"],undefined,true]}]';


// jsonParser(jsonStrSimple);
// jsonParser(jsonStrWithObj);
// jsonParser(jsonStrWithObj2);
jsonParser(jsonStr);

// jsonParser(jsonStrWithArr);
// jsonParser(jsonStrWithArr2);
// jsonParser(jsonStrWithArr3);

// jsonParser(arrJson);
// jsonParser(arrJson1);
// jsonParser(arrJson2);

Keywords: Javascript stack

Added by dreamwest on Mon, 10 Jan 2022 11:08:04 +0200