Javascript is required
踩坑避坑发布于 2026-07-28审校于 2026-08-089 分钟阅读

嵌套 JSON 反斜杠为什么变多:分层转义与解析

日志或接口中出现 {"payload":"{\"name\":\"Tiny\"}"},通常不是“反斜杠失效”,而是 JSON 文本又被放进了另一个 JSON 字符串。每跨过一层字符串语法,双引号和反斜杠都必须再次转义。本文只处理嵌套 JSON 的表示层问题,不讨论普通 JSON 语法错误或 Unicode \uXXXX 的编码原理。

JSON转义反斜杠JSON.stringify嵌套 JSON

一、先区分值、JSON 文本和源码字面量

同一份数据至少可能以三种形态出现:内存中的对象、JSON.stringify 返回的文本、写在 JavaScript 源码中的字符串字面量。开发者看到的斜杠数量取决于当前观察的是哪一层。

控制台和调试器为了展示字符串边界,可能使用带转义的预览。判断真实内容时,应同时查看 typeof、字符串长度以及 console.log 的直接输出,不要只看对象预览中的引号。

const value = { message: 'He said "hello"', path: 'C:\\temp\\a.txt' };
const jsonText = JSON.stringify(value);

console.log(typeof value);    // "object"
console.log(typeof jsonText); // "string"
console.log(jsonText);
// {"message":"He said \\"hello\\"","path":"C:\\\\temp\\\\a.txt"}

二、反斜杠增加是表示层嵌套的必然结果

如果外层对象的 payload 字段要求保存一段“JSON 文本”,就需要先 stringify 内层对象,再 stringify 外层对象。第二次序列化必须保护内层文本里的双引号和反斜杠,因此日志中会看到更多斜杠。

这些斜杠不是业务数据的一部分,而是当前 JSON 文本用来表达字符串内容的语法字符。每正确 parse 一层,就会移除该层对应的转义。

const payloadObject = {
  name: 'Tiny',
  path: 'C:\\temp\\a.txt',
};

const envelopeObject = {
  event: 'save',
  payload: JSON.stringify(payloadObject),
};

const requestBody = JSON.stringify(envelopeObject);
console.log(requestBody);
// {"event":"save","payload":"{\\"name\\":\\"Tiny\\",\\"path\\":\\"C:\\\\temp\\\\a.txt\\"}"}

三、正确解析:一层字符串对应一次 JSON.parse

先解析 requestBody 得到外层对象,此时 payload 仍是 string;只有接口契约明确规定 payload 是字符串化 JSON 时,才对 payload 再解析一次。

不要写“反复 parse 直到不报错”的循环。普通字符串如 "123""true" 或用户输入也可能恰好是合法 JSON,递归猜测会悄悄改变类型。

type Envelope = {
  event: string;
  payload: string;
};

type Payload = {
  name: string;
  path: string;
};

const requestBody = JSON.stringify({
  event: 'save',
  payload: JSON.stringify({ name: 'Tiny', path: 'C:\\temp\\a.txt' }),
});

const outer = JSON.parse(requestBody) as Envelope;
const inner = JSON.parse(outer.payload) as Payload;

console.log(inner.name); // "Tiny"
console.log(inner.path); // "C:\\temp\\a.txt"

四、编码端应基于对象序列化,禁止手工拼接

手工拼 JSON 时,用户输入中的引号、反斜杠、换行和控制字符都会破坏语法。正确流程是先构造对象,再由 JSON.stringify 负责每一层转义。

更理想的接口设计是让 payload 直接保持对象,而不是字符串化 JSON。只有消息队列字段、数据库文本列、签名协议或第三方接口明确要求字符串时,才保留双层编码。

const payload = { query: 'name = "Tiny"', path: 'C:\\data' };

// 优先:payload 直接是对象,只需要序列化一次
const preferredBody = JSON.stringify({
  event: 'search',
  payload,
});

// 仅在协议要求 payload 为字符串时使用
const legacyBody = JSON.stringify({
  event: 'search',
  payload: JSON.stringify(payload),
});

五、解析不可信字段时使用明确的字段级函数

当接口历史数据导致同一字段可能是对象或字符串时,可以在边界层做一次兼容,但必须限定字段和最大层数,并保留失败结果用于告警。不要对整个响应递归扫描所有字符串。

type JsonObject = Record<string, unknown>;

function parseObjectField(value: unknown): JsonObject {
  if (value && typeof value === 'object' && !Array.isArray(value)) {
    return value as JsonObject;
  }

  if (typeof value !== 'string') {
    throw new TypeError('payload must be an object or a JSON object string');
  }

  const parsed: unknown = JSON.parse(value);
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new TypeError('payload JSON must contain an object');
  }

  return parsed as JsonObject;
}

六、为什么 replace(/\\/g, "") 会破坏数据

全局删除反斜杠不仅会移除 JSON 语法转义,还会破坏 Windows 路径、正则文本、换行序列以及字符串中本来需要保留的反斜杠。把 \" 直接替换成 " 也无法判断它属于哪一层。

如果 JSON.parse 报错,应保留原始文本并按 /articles/json-unexpected-token 的流程定位。只有明确知道输入是某种非标准转义协议时,才能编写针对该协议的转换器。

  • 不要删除所有反斜杠。
  • 不要对整个响应连续 JSON.parse,直到结果不再是字符串。
  • 不要用字符串是否以 { 开头作为唯一类型判断。
  • 不要把控制台展示中的转义形式误认为网络传输内容。

七、边界条件与验证方法

测试样本至少包含双引号、反斜杠、换行、Tab、空字符串、null、数组以及 Unicode 字符。验证标准不是“日志看起来斜杠变少”,而是最终对象与最初对象深度相等。

对于需要签名或哈希的协议,还必须明确签名的是对象语义、内层 JSON 文本还是外层请求体;不同空格、键顺序和转义形式会生成不同字节。

const original = {
  quote: '"',
  slash: '\\',
  newline: '\n',
  nested: { text: '中文 🚀' },
};

const outerText = JSON.stringify({ payload: JSON.stringify(original) });
const decodedOuter = JSON.parse(outerText) as { payload: string };
const restored = JSON.parse(decodedOuter.payload);

console.assert(JSON.stringify(restored) === JSON.stringify(original));

八、FAQ 与结论

问:为什么 Postman、Network 和 console 显示的斜杠数量不同?答:它们可能分别展示原始字节、JSON 文本或调试器的字符串预览。

问:payload 能不能永远直接定义为对象?答:自有 API 通常可以;但数据库文本字段、消息协议或第三方接口可能明确要求字符串。

问:\u4e2d 也是嵌套 JSON 问题吗?答:不一定,它是 JSON Unicode 转义,原理见 /articles/unicode-json-escapes

结论:反斜杠数量由表示层决定。编码时每层只 stringify 一次,解码时根据契约每层只 parse 一次;能传对象就不要额外字符串化。可使用 /tools/json/json-compress-escape 检查转义结果。

来源与延伸阅读

技术审校所依据的规范与权威参考资料。

相关文章

继续阅读

可打开关联的浏览器工具,使用自己的样本验证文中的处理流程。

打开关联工具