教程 · 语言

JS / Python / Go 解析 JSON

同一段合法 JSON,三种语言读出来的「对象」并不一样。联调时经常出现:前端觉得字段没了,Go 觉得类型对不上,Python 日志里中文全是 \\u。先把默认行为搞清楚,再怪接口。

JavaScript

浏览器和 Node 用 JSON.parse / JSON.stringify。HiJSON 也在这个语义上工作,所以你在树上看到的,基本就是 JS 解析后的值。

JSON.parse('{"a":1}')
JSON.stringify({ a: 1, b: undefined, c: () => {} })
// '{"a":1}'  —— b、函数都丢了
  • undefined、函数、Symbol 不会出现在输出里。
  • Date 会被 toJSON 成 ISO 字符串,对端若当时间戳数字,对不上。
  • NaN / Infinity 变成 null
  • 大于 Number.MAX_SAFE_INTEGER 的整数会丢精度。雪花 ID、订单号用字符串。
  • 要保留大整数,得上 json-bigint 一类库,或让接口改类型。

JSON.parse 的第二个参数 reviver 可以改键。写了 reviver 再和「原始文本」对比,别忘了自己改过。

Python

import json
json.loads('{"name":"测试"}')
json.dumps({"name": "测试"})          # 默认 ensure_ascii=True,中文变 \\u
json.dumps({"name": "测试"}, ensure_ascii=False)
  • 对象变成 dict,数组变成 listtrue/false/null 变成 True/False/None。打印出来不像 JSON,不要把 repr(dict) 当 JSON 发出去(单引号)。
  • datetime 不能直接 dumps,要 default=str 或自定义 encoder。
  • 数字:JSON 里的整数在 Python 3 可以任意大;和 JS 对同一字段时,JS 可能已经漂了。
  • json.loads 遇到重复键,后写的覆盖先写的,和多数 JS 引擎类似。

从日志复制 Python dict 到 HiJSON,先换成双引号,或在 Python 里 print(json.dumps(obj, ensure_ascii=False)) 再复制。

Go

type Order struct {
    ID     string  `json:"order_id"`
    Amount float64 `json:"amount"`
    Note   string  `json:"note,omitempty"`
}
  • 字段要导出(大写)才会被编码。小写字段静默消失,这是新手第一坑。
  • tag 决定线上键名。order_id 和结构体里的 ID 不是一回事。
  • omitempty:零值不输出。 false0"" 都会省掉。前端以为「没这个字段」,其实是零值被吃了。
  • 没有 tag 的数字,解码进 interface{} 时是 float64。整数 1 变成 1.0,再 Marshal 可能带小数点。
  • 未知字段默认丢掉。要严格,用 Decoder.DisallowUnknownFields

Go 的 json.Number 可以保原文。和前端对大整数时,结构体用 stringjson.Number,不要用 int64 去接已经在 JS 里漂过的数。

对照表

情况JSPythonGo
字段值为 undefined / 未设置stringify 丢掉没有 undefined,用缺键或 Noneomitempty 可能丢掉零值
中文默认原文默认 \\uXXXX默认原文
超大整数精度危险int 够用看类型,interface 是 float64
时间Date → ISO 字符串要自己编码time.Time → RFC3339

联调时怎么用 HiJSON

把各端实际发出的 body 分别贴进 HiJSON,不要对比「语言里的对象打印」。看三件事:

  1. 键在不在(omitempty / undefined)。
  2. 类型是数字还是字符串(金额、ID)。
  3. 列表长度是否被单对象吃掉。

路径对齐用 JSONPath,见 JSONPath 入门。结构约定可以写成 schema,见 JSON Schema

常见问题

为什么 Go 收得到、Chrome 控制台里数字不对?
先在 HiJSON 里看原始文本里的数字有多少位。超过 15–16 位有效数字,JS 已经圆整。问题在浏览器,不在 Go。
Java / PHP 呢?
套路类似:类型系统和「缺字段 / null / 零值」三套语义。先抓实际 JSON 文本,再对语言文档。不要用语言内部的 debug 打印当契约。

相关教程

对比各端真正发出的 JSON

以文本为准,不以 console.log 的对象展开为准。

打开 HiJSON →