JSON Formatter:API 调试中的证据、结构和差异排查
围绕 API 调试中的 JSON Formatter 工作流,说明如何保留原始 payload、校验语法、查看嵌套结构、处理转义 JSON、用 diff 对比改动,并在分享示例前脱敏敏感字段,避免把人为修改、日志前缀或二次转义当成接口问题。适合接口联调、日志排查和工单复现。适合开发排查。
一次 API 联调失败,表面上经常只是“服务端返回 400”或“前端解析失败”。但真正要查的不是 JSON 好不好看,而是原始 payload 到底长什么样、它是不是合法 JSON、字段结构是否符合接口契约,以及失败样例和成功样例之间有哪些肉眼不容易发现的差异。JSON Formatter 在这类场景里最有价值的用法,是把证据变成可读结构,而不是替你猜业务原因。
下面是一套更接近真实排查的工作流。它适合接口联调、webhook 投递失败、浏览器 Network 响应异常、线上日志复盘,也适合把问题整理成可以交给后端、第三方 API 或客户支持团队的最小样例。
第一件事:先冻结原始证据
不要在唯一一份失败响应上直接格式化、删字段、补逗号或手动改引号。先保存原始文本,再复制一份用于分析。原始文本最好包含来源:请求 URL、方法、状态码、响应头、请求体、响应体、时间点和环境名称。很多问题最后不是 JSON 字段本身,而是复制来源、编码层、日志截断或代理层改变了内容。
一个常见场景是:浏览器 Network 面板里响应能展开,但应用代码里 JSON.parse 报错。此时要分别保存“Network 原始响应”和“应用收到的字符串”。如果后者来自日志系统,日志可能已经把换行、引号或反斜杠二次转义;如果来自 webhook 控制台,控制台可能只展示截断后的 body。只有保留原始证据,后面用 formatter 发现的错误才有上下文。
建议至少保留三份材料:
- 原始 payload:一字不改,用于回溯事实;
- 格式化副本:用于阅读结构和定位字段;
- 最小复现样例:只保留触发问题所需字段,用于沟通和测试。
这三份材料用途不同。把它们混成一份,往往会把“事实”“分析”和“修复尝试”搅在一起。
先判定是不是 JSON,再谈字段含义
把文本放进 formatter 后,先看语法是否成立。合法 JSON 有严格边界:对象 key 必须使用双引号,字符串不能用单引号,数组或对象最后一项后不能有 trailing comma,字符串内部真实换行必须转义,反斜杠也要按 JSON 规则转义。很多从 JavaScript 示例、配置文件或日志里复制出来的内容,看起来像 JSON,但实际是 JavaScript object、带注释的配置片段,或者已经被日志层包了一次的字符串。
排查时不要同时改十个地方。解析器报第一个错误位置时,先检查该行、上一行,以及最近的 {、[、引号和逗号。前面少一个引号,后面可能连续报很多无关错误;先修第一个错误,重新校验,再看第二个错误是否仍存在。
判断标准很简单:
- 如果 formatter 无法解析,当前任务是修复或还原合法 JSON 文本;
- 如果 formatter 可以解析,当前任务才进入字段、类型、嵌套和业务契约检查;
- 如果原始文本不是 JSON,而是日志行、HTTP multipart、JWT、Base64 或转义字符串,就先确认外层格式,不要强行当 JSON 分析。
这一步能避免一个很常见的误判:把“复制出来的样例坏了”误当成“接口真的返回坏 JSON”。
格式化后看结构,不要只看缩进
格式化完成后,重点看字段关系,而不是缩进本身。实际联调中最容易造成前后端分歧的差异通常有这些:
| 可疑点 | 例子 | 为什么会出问题 |
|---|---|---|
| 类型漂移 | "amount": "19.90" 与 "amount": 19.9 |
前端计算、schema 校验或数据库写入可能按不同类型处理 |
| 缺失字段 | 成功样例有 currency,失败样例没有 |
默认值不一定在所有服务里一致 |
| null 与空字符串 | null、""、字段不存在 |
三者在业务含义和序列化结果上不同 |
| 数组与对象混用 | items: [] 与 items: {} |
循环渲染、反序列化和 schema 都可能失败 |
| 嵌套层级变化 | data.user.id 变成 data.account.user.id |
调用方路径读取会失效 |
| 数字精度 | 大整数 ID 被当数字 | JavaScript 可能丢失精度,应按字符串处理 |
例如支付回调里 amount 字段从数字变成字符串,肉眼看一行压缩 JSON 很难发现;格式化后放在同一层级,就能直接看到类型不同。再比如某个字段值是 null,不是空字符串,也不是缺失字段。很多 bug 的根源就在这些“看起来差不多”的差异里。
用 diff 比较成功样例和失败样例
如果你手里有一份正常响应和一份失败响应,分别格式化后再用 Text Diff 对比。不要直接比较压缩成一行的 payload。格式化后的 diff 更容易暴露字段顺序之外的真正差异,例如某个数组多了一项、某个对象少了一层、某个布尔值从 true 变成字符串 "true"。
对比时按这个顺序看:
- 顶层结构是否一致:都是 object,还是一个是 array;
- 状态字段是否一致:
status、code、success是否被不同服务解释; - 数据字段是否同名同层级:尤其是
data、result、payload这类包装层; - 失败样例是否多了错误对象:有些接口会在错误时完全换一个 schema;
- 字段类型是否漂移:字符串、数字、布尔、null、数组、对象要逐项看。
如果 diff 里出现大量顺序变化,先不要急着下结论。JSON 对象字段顺序通常不应作为业务语义。真正要关注的是字段是否存在、类型是否改变、值是否落在允许范围内。
处理“JSON 里面还有一段 JSON”
线上日志和第三方接口经常出现包装层。你可能看到这样的字段:
{
"event": "payment.failed",
"rawPayload": "{\"orderId\":\"A1001\",\"reason\":\"card_declined\"}"
}
这里 rawPayload 是字符串,不是对象。第一层 JSON 合法,并不代表内层字符串已经作为 JSON 对象被应用读取。排查时先确认生产方是否本来就设计成“字符串里存 JSON”。如果是,再把内层字符串还原后单独校验;如果不是,就应该回到序列化流程,检查是不是多做了一次 JSON.stringify。
Base64 包装也类似。有些 API 会把 JSON 片段放进 Base64 字段中传输。只有在文档、字段名或上游说明明确表明它是 Base64 时,才用 Base64 Encoder 解码后继续格式化。不要看到一串长字符就猜它是 JSON;它也可能是签名、加密结果、压缩数据或随机 ID。
脱敏时保留会触发 bug 的形状
API 排查经常需要把 payload 发给同事、供应商或支持团队。生产数据必须脱敏,但脱敏不能把问题一起删掉。好的脱敏应该保留字段名、层级、类型、数组长度特征和触发错误的边界值。
例如:
- token 可以替换成
REDACTED_TOKEN,但不要改成数字; - 邮箱可以替换成
user@example.com,但如果问题和特殊域名有关,要保留相同形状; - 长字符串导致接口超限时,要用同等长度级别的模拟字符串;
- 数组长度触发分页或批量处理 bug 时,不要把 200 项删成 2 项后再报问题;
- 字段缺失导致失败时,不要为了样例完整而补上字段。
脱敏后的样例仍应能解释“为什么失败”。如果脱敏后已经不能复现,那它只能当截图材料,不能当调试样例。
一套可执行的 API JSON 排查流程
实际遇到失败请求时,可以按下面的顺序走:
- 保存原始请求、原始响应和来源信息;
- 复制副本,先用 formatter 校验是否为合法 JSON;
- 如果非法,只修第一个语法错误,判断是接口返回问题还是复制/日志问题;
- 如果合法,格式化后检查顶层结构、字段类型、null/缺失字段和嵌套层级;
- 找一份成功样例,格式化后做 diff;
- 对包装字段单独判断:转义 JSON、Base64、JWT、签名、加密不要混为一谈;
- 脱敏并保留触发问题的类型、长度和结构;
- 最后再对照 API schema、OpenAPI 文档或服务端验证规则。
Formatter 在这套流程里扮演的是“结构显微镜”。它能帮你看清 payload,但不能替代 schema、权限、业务状态和服务端日志。
FAQ
JSON 能格式化成功,为什么 API 还是返回 400?
格式化成功只说明语法合法。API 还可能要求字段必填、类型准确、枚举值有效、时间格式正确、签名匹配、用户有权限,或者请求状态符合业务流程。
可以直接修 formatter 提示的错误再发给后端吗?
可以把修复后的样例作为分析材料,但不要把它当作线上事实。先说明哪些内容是原始 payload,哪些是你为了校验手动修过的副本。
为什么同一个字段有时是 null,有时不存在?
这通常是接口契约问题。null 表示字段存在但值为空;字段不存在表示生产方没有输出它。前端、反序列化库和 schema 校验可能对两者做不同处理。
在线工具里能粘贴生产响应吗?
只应粘贴脱敏后的副本。token、签名、邮箱、客户 ID、内部 URL、订单号、租户名和任何个人数据都应先移除或替换,并确认脱敏没有改变触发问题的结构。