DEBUG.WIKI
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
雨云;给项目一台云服务器
晚安云;香港 CN2 ¥15/月

Ajv compileParser 解析空白空数组,返回 unexpected token ]

JTD字符串数组接受[],加上合法空格却解析失败。Ajv 8.20.0仍有对应分支,可在限定schema中拆成JSON.parse与JTD校验,并调整错误返回协议。

同一个 JTD schema,解析 {"data":[]} 正常,输入 {"data":[ ]} 却返回 undefined,message 是 unexpected token ]。空格在 JSON 数组中合法,先确认使用的是 Ajv 的编译解析器 compileParser(),而不是普通 JSON.parse 后的校验函数。

具体故障出现在 Ajv 8.18.0 的字符串数组字段。当前正式 8.20.0 的 parseElements 仍沿用相同处理:读过左方括号后直接进入元素循环,尚未先跳过空白。此时看到空格便尝试解析一个字符串元素,跳过空白后又碰到了右方括号,因而失败。

不要把解析失败当成空数组

编译解析器在语法或数据结构不符合要求时返回 undefined,错误通过解析函数的 message、position 提供;它不像 JSON.parse 那样一定抛异常。只在外层捕获 SyntaxError,可能完全接不到这次失败。

应先保存这次解析的错误和位置,再处理下一份输入,因为这些属性会被后续调用更新。不要直接把 undefined 替换成 { data: [] },否则缺字段、字段类型错误和真正无效的 JSON 都可能被当成合法空结果。

这条分析针对 elements 为 string 的具体形态,以及左括号后有空白、接着直接结束的数组。不能把所有 unexpected token ] 都归为同一根因;非空数组中的错误元素、缺少引号或多余逗号仍应按原始输入检查。

可以暂时分开解析与 JTD 校验

对于下面这份简单 schema,可以让原生 JSON.parse 先处理 JSON 语法,再由 Ajv 的 JTD validator 检查对象形状。以下配置不启用日期转换或其他特殊解析选项:

import Ajv from 'ajv/dist/jtd.js';

const ajv = new Ajv();
const schema = {
  properties: {
    data: { elements: { type: 'string' } },
  },
};
const validate = ajv.compile(schema);

function readDataJson(text) {
  const value = JSON.parse(text);
  if (!validate(value)) {
    throw new TypeError(ajv.errorsText(validate.errors));
  }
  return value;
}

这条入口仍需同时处理 JSON 语法异常和数据校验失败,不能只因 JSON.parse 成功就接收对象。schema 在这里是完整、合法的 JTD 对象,data 仍然必填,数组中的每一项仍必须为字符串。

这会改变原来的错误协议:调用方现在面对异常,而非统一的 undefined、message、position 三项信息。应在现有接口边界转换为约定的错误格式,不将异常直接泄露给外部用户。两步处理也不应被描述为与专用解析器在所有配置下完全等价;复杂 schema、timestamp、重复键及自定义选项要按原契约另核。

不要用正则把整段 JSON 的空白删光。字符串里的空格、换行转义和其他内容属于数据,粗暴清理可能改变值。调用私有编译函数、直接替换生成代码,也会给下一次升级留下难以核对的分支。

在受控流程保留 []、[ ]、含换行的空数组、正常字符串数组、含数字的数组和畸形 JSON。前三种合法空数组应得到相同业务结果,错误元素仍须拒绝;不再出现该条报错只是其中一项检查。正式依赖更新后,再核对这组输入与接口错误格式,不能因为小版本变化就删除原有校验。