JSON String 格式化函数

发布时间 2023-12-20 11:00:39作者: 前端小白的江湖路

背景

一般我们格式化JSON string是通过JSON.parse, 再使用JSON.stringify方法进行格式化,但是JSON.parse 有BigInt精度丢失问题

方案

考虑用以下纯字符串的方式来解析,添加空格与\n来缩进。

比较易漏的点在于

1.考虑转义字符,

2.字符串内的字符,不需要格式化,需要优先跳过。

/**
 *
 * @param jsonStr {string} JSON字符串格式化函数,去除多余的空格,\n等
 * @param indentChar {string} 缩进字符,默认为2个空格缩进
 * @returns string
 */
export const jsonPrettier = (jsonStr = "", indentChar = "  ") => {
  if (typeof jsonStr !== "string") {
    console.warn(`jsonStr: ${jsonStr} 需要为字符串类型`);
    return jsonStr;
  }
  if (typeof indentChar !== "string") {
    throw new Error(`indentChar: ${indentChar}缩进字符需要为字符串类型`);
  }

  let result = "";
  let isInString = false; // key, value, array子项
  const SPACE_CHAR = " ";
  const ESCAPE_CHAR = "\\";
  let indentLevel = 0;
  let i = 0;

  while (i < jsonStr.length) {
    const item = jsonStr[i];

    // 跳过非字符串内的转义字符
    if (item === "\n" || item === "\t" || item === "\r" || item === "\b") {
      i++;
      continue;
    }

    // 字符串内存在转义字符
    if (item === ESCAPE_CHAR) {
      const nextChar = jsonStr[i + 1] ?? "";
      result += item + nextChar;
      i += 2;
      continue;
    }

    if (item === '"') {
      result += item;
      isInString = !isInString;
      i++;
      continue;
    }

    if (isInString) {
      result += item;
      i++;
      continue;
    }

    // 不在string中
    switch (item) {
      case SPACE_CHAR:
        i++;
        continue;

      case "{":
      case "[":
        result += `${item}\n`;
        indentLevel += 1;
        result += indentChar.repeat(indentLevel);
        break;

      case "}":
      case "]":
        result += "\n";
        indentLevel -= 1;
        result += indentChar.repeat(indentLevel) + item;
        break;

      case ",":
        result += item + "\n";
        result += indentChar.repeat(indentLevel);
        break;

      case ":":
        result += item + SPACE_CHAR;
        break;

      default:
        result += item;
    }
    i++;
  }

  return result;
};

const test = (jsonStr = "") => {
  const a = JSON.stringify(JSON.parse(jsonStr), null, 2);
  const b = jsonPrettier(jsonStr);
  console.warn("is equal: ", a === b);
  console.log(`origin jsonStr: `, jsonStr);
  console.log("prettier result: ", b);
  console.log("----------");
  if (a !== b) {
    throw new Error("a与b不相等");
  }
};

// number string boolean array object null undefined
test("100 ");
test("true ");
test('"abc "  ');
test('{"a":    10,    "b": 100}');
test('["a", "b", "{abcd}\\""]');
test('{ \r  "a": 1, "b,,,": \n"bb: b", "c": { "d": 100,\n "e": "abc\\n"}}\n');
test(
  `{ \r  "a": 1, "b,,,": \n  
   "bb: b", "c": \t    \t
   { "d": 100,\n "e": "abc\\n"}}\n`
);
test(`{
  "a": "a b c"
}`);

输出结果: