JSON.stringify()的几个场景

发布时间 2023-04-13 19:29:03作者: 风行者夜色

循环引用

使用JSON.stringify()时,遇到循环引用的时候,会抛出错误TypeError: Converting circular structure to JSON,如果需要强行转成字符串的话,需要利用到该方法的第二个参数。

主要思路其实就是将循环引用的部分替换成某个标识,等到解析的时候去替换掉,就可以拿到原来的循环引用的对象。通常是为了在输出json文件后,依然可以得到原来的循环引用的对象来处理。在此也就是抛砖引玉,不做更多的探讨,下面的实现肯定有遗漏的地方,主要就是记录一下思路。

// 先讲环的部分按照自己的格式存起来,并且记录环指向的对象
const JSONStringify = (obj) => {
  const cache = new Map();
  // 额外套一层,就是为了防止循环引用的是最外层的对象,无法去记录对应的key值
  const newObj = {
    entry: obj
  };
  return JSON.stringify(newObj, (key, value) => {
    if(typeof value === 'object' && value !== null) {
      if(cache.has(value)) {
        return `[[object_${cache.get(value)}]]`;
      }
      cache.set(value, key);
    }
    return value;
  })
};

// 用来专门解析上面产生的字符串
const JSONParse = (objStr) => {
  const parseObj = JSON.parse(objStr);

  let cache = {};
  const ergodic = (obj) => {
    for(const [key, value] of Object.entries(obj)) {
      // 匹配到了环的部分就去替换
      if(/\[\[object_\w+\]\]/.test(value)) {
        const [,circleKey] = value.slice(2, -2).split('_');
        obj[key] = cache[circleKey];
      }
      // 记录所有的对象
      if(typeof value === 'object' && value !== null) {
        cache[key] = value;
        ergodic(value);
      }
    }
  };

  ergodic(parseObj);

  return parseObj.entry;
};

const obj = {
  a: {
    b: 1
  }
};

obj.a.c = obj;

const objStr = JSONStringify(obj); 
// {"entry":{"a":{"b":1,"c":"[[Object_entry]]"}}}
const newObj = JSONParse(objStr);
// { a: { b: 1, c: [Circular *1] } }

大JSON的处理

当 JSON.stringify 所生成的字符串长度超过了一定大小的限制后,会抛出Uncaught RangeError: Invalid string length错误。

我自己的思路大概有两个,一个是拆开去转化,另一个就是使用数据流的形式去读写,目前用第一种方式也能满足我的场景。正常来说,拆开就可以防止一次的JSON.stringify占据的内存过大,如果拆开一层还不行,可以试试递归几次应该就满足了。

const bigJSONStringify = (obj) => {
  let result = '';
  if(Array.isArray(obj)) {
    result += '[';
    for(let value of obj) {
      if(typeof value === 'object' && value !== null) {
        result += JSON.stringify(value);
      } else {
        result += value;
      }
      result += ','
    }
    result = result.slice(0, -1) + ']';
  } else {
    result += '{';
    for(const [key, value] of Object.entries(obj)) {
      if(typeof value === 'object' && value !== null) {
        result += `"${key}": ${JSON.stringify(value)}`;
      } else {
        // 字符串类型加双引号
        result += `"${key}": ${typeof value === 'string' ? `"${value}"` : value}`;
      }
      result += ','
    }
    result = result.slice(0, -1) + '}';
  }
  return result;
};
bigJSONStringify([{ a: 'a', v:2, d: { m: 3}}, 2, { c: 1, d: 3 }])
// [{"a":"a","v":2,"d":{"m":3}},2,{"c":1,"d":3}]
bigJSONStringify({ a: 'a', v:2, d: { m: 3}})
// {"a": "a","v": 2,"d": {"m":3}}

格式化写入到文件中的json

经常发现写入到文件中的json是挤在一起的,就想着有没有可能在写入的时候就格式化一下,发现JSON.stringify是提供了的,就是第三个参数,为数字的话,就是缩进的格数,为字符串的话,就是用这个字符串替代缩进,不过都有一个最大值的限制,数字不能大于10,字符串超过10的长度就取前10个字符。

fs.writeFileSync('./test.json', JSON.stringify({ a: 1, b: 2, c: 3 }, null, 4), 'utf-8');
//{
//    "a": 1,
//    "b": 2,
//    "c": 3
//}
fs.writeFileSync('./test.json', JSON.stringify({ a: 1, b: 2, c: 3 }, null, '----'), 'utf-8');
//{
//----"a": 1,
//----"b": 2,
//----"c": 3
//}