不规范 JSON 怎么办?三种修复思路+代码实现

0 阅读5分钟

最近在处理一批第三方返回的数据时,我遇到了一个令人头疼的问题:部分 JSON 字符串是破损的,导致在用 JSON.parse() 解析时直接抛出异常。比如有些缺少引号,有些多了逗号,甚至还有嵌套结构不完整的情况。手动修复显然不现实,于是我开始寻找是否有自动修复 JSON 的方案或工具,比如能否在解析前做一些预处理,或者使用更智能的解析库来容错。那面对这些结构混乱的 JSON 数据,我们到底该怎么安全地修复和解析它们呢?

在JavaScript中,正常来说我们都是使用JSON.parse方法将JSON字符串转换为JavaScript对象。以下是一个简单的示例:

// JSON字符串
const jsonString = '{"name":"Alice","age":25,"city":"Wonderland"}';

// 使用JSON.parse将JSON字符串转换为JavaScript对象
const jsonObject = JSON.parse(jsonString);

// 打印转换后的对象
console.log(jsonObject);

// 访问对象的属性
console.log('Name:', jsonObject.name);
console.log('Age:', jsonObject.age);
console.log('City:', jsonObject.city);

这世界就像一个草台班子,总有些奇奇怪怪的内容,有时候,拿到一段别人写的 JSON,就像接手一锅没煮熟、还撒了一地的乱粥

  • 少了引号
  • 多了逗号
  • 布尔值写成大写True 而不是 true
  • 结尾少了大括号……

你拿这种东西直接 JSON.parse()?对不起,JS 直接报错扔锅!💥 那怎么办?当然是「修粥大法」上场!

我们需要从一个老旧的系统获取数据,而这个系统就有意思了,他的接口返回的是一个纯字符串,如果是纯正json字符串,我转成对象处理也可以,结果他这个返回字符串在转json的时候还报错了,但有时候会出现各种奇怪的错误——单引号代替了双引号,属性名没有加引号,或者多了一些奇怪的逗号或注释……

原生JavaScript方法:手动调锅,自己加水加火加料

我首先尝试了最简单的方法——使用try/catch和一些基本的字符串替换:

function tryParseJSON(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch (e) {
    // 尝试简单修复:去除可能的多余字符
    const cleaned = jsonString.replace(/[^\x20-\x7E]/g, '').trim();
    try {
      return JSON.parse(cleaned);
    } catch (e2) {
      console.error("无法解析JSON:", e2);
      return null;
    }
  }
}

这种方法对于简单的问题有时能奏效,但对于更复杂的错误,比如混合了单引号和双引号的情况,就力不从心了。我接着尝试了一个更针对性的修复方法:

function fixQuotes(jsonString) {
  // 将单引号替换为双引号(简单情况)
  return jsonString.replace(/'/g, '"');
}

但问题是,这样的替换太过简单粗暴,如果字符串内容中本身包含单引号,就会导致新的解析错误。难道真的没有更好的解决方案吗?


在搜索了大量资料并尝试了各种方法后,我找到了两个非常有用的库:jsonrepair 和 JSON5。这两个库在处理破损的JSON字符串时各有所长。

jsonrepair: 智能修锅机器人!

jsonrepair 是我发现的一个专门用于修复损坏JSON的强大工具。它能处理多种常见的JSON格式错误:

npm install jsonrepair

示例代码

const { jsonrepair } = require('jsonrepair');

const damagedJson = "{'name': 'John', age: 30}";
const fixedJson = jsonrepair(damagedJson);
const obj = JSON.parse(fixedJson);

console.log(obj); // { name: 'John', age: 30 }

这个库能修复什么类型的问题呢?实际上相当广泛:

  • 引号问题:将单引号替换为双引号,为未加引号的属性名和字符串值添加引号
  • 多余字符:移除注释、尾部逗号和JSON前后的无关字符
  • 结构问题:修复缺少闭合的括号或引号
  • 编码问题:修复转义字符和Unicode字符

但是,即使是这样强大的工具也有其局限性。对于严重损坏的JSON(如完全无效的语法、深层嵌套的结构错误),jsonrepair 也无能为力。这时候,我们需要另一种解决方案。

JSON5:允许你“不按食谱炒菜”的宽松厨房

在某些情况下,我们需要的不是修复现有的JSON字符串,而是使用一个更宽松的解析器。这就是 JSON5 派上用场的时候:

npm install json5

示例代码

const JSON5 = require('json5');

const looseJson = `{
  // 这是一个注释
  name: 'Tom',
  age: 30,
  traits: ["smart", 'funny'],
}`;

const obj = JSON5.parse(looseJson);
console.log(obj);

JSON5 允许:

  • 单引号字符串
  • 未加引号的属性名
  • 尾随逗号
  • 注释(单行和多行)
  • 更灵活的数字格式、十六进制、科学计数法等数字格式
  • 多行字符串

但它也不是万能的。JSON5 不能处理完全不相关的格式或严重结构损坏的数据。

组合使用多种工具

在经过多次尝试后,我发现最有效的方法是根据情况组合使用不同的工具。下面是我最终采用的解决方案:

function parseJson(jsonString) {
  // 第一步:尝试标准JSON解析
  try {
    return JSON.parse(jsonString);
  } catch (e) {
    console.log("标准JSON解析失败,尝试修复...");
    
    // 第二步:尝试使用jsonrepair修复
    try {
      const { jsonrepair } = require('jsonrepair');
      const fixedJson = jsonrepair(jsonString);
      return JSON.parse(fixedJson);
    } catch (e2) {
      console.log("修复失败,尝试使用JSON5解析...");
      
      // 第三步:尝试使用JSON5解析
      try {
        const JSON5 = require('json5');
        return JSON5.parse(jsonString);
      } catch (e3) {
        // 最后:如果所有方法都失败,返回错误信息
        console.error("所有解析方法都失败了:", e3);
        throw new Error("无法解析JSON数据");
      }
    }
  }
}

虽然有了 jsonrepair 和 JSON5 这样的“补救工具”,但它们永远只是权宜之计。我们应该追本溯源,确保数据的生成过程符合 JSON 标准。如果你需要处理来自用户或第三方的非标准数据,建议在入口阶段就做格式校验和错误报告。