JSON 格式化完整教學 2026:從基礎到進階、整合 SuperTools 線上格式化
寫給:每天在 Postman、瀏覽器 DevTools、API 文件之間切換的前後端工程師、資料分析師,以及需要看懂 webhook payload 的 PM。
JSON(JavaScript Object Notation)是現代 Web 的通用語。從 REST API 的請求/回應、Kafka 訊息、設定檔,到 NoSQL 文件儲存,JSON 幾乎是預設選項。但每天打開一坨壓縮過、沒換行的 JSON 字串時,多數工程師仍然會皺眉。本文用 14 分鐘帶你完整掌握 JSON 格式化的核心觀念、實戰技巧、踩過的坑,以及如何用 SuperTools 線上工具在 10 秒內把醜陋的 payload 變成乾淨易讀的階層結構。
1. JSON 是什麼
JSON 由 Douglas Crockford 於 2001 年正式提出,2013 年成為 ECMA-404 標準、2017 年成為 RFC 8259。它的設計目標只有一個:人類可讀、機器可解析的最小資料交換格式。
JSON 的語法骨幹只有六個基本型別:
object:以{}包起來的 key-value 集合array:以[]包起來的有序列表string:以雙引號包起來的 Unicode 字串number:整數或浮點數(不支援NaN、Infinity)boolean:true/falsenull:空值
注意三件事:
- key 一定要用雙引號(
'key'或不加引號都是 JavaScript 物件字面值、不是 JSON) - 不允許尾隨逗號(trailing comma)
- 不允許註解(
//或/* */)—— 想要註解請改用 JSON5、JSONC
合法的最小 JSON 範例:
{
"name": "蘇曉雯",
"age": 50,
"skills": ["護膚", "創業"],
"active": true,
"spouse": null
}
JSON 在後端服務之間幾乎是預設交換格式,主要原因有三:
- 結構直接對應到所有主流語言的 dict / map
- 文字格式 debug 容易(不像 Protobuf 要 schema)
- 體積在文字協定中相對小(比 XML 省 30%~60%)
2. 5 個核心觀念
2.1 字串編碼一定是 UTF-8
RFC 8259 明確規定 JSON 字串以 UTF-8 為傳輸編碼。中文、emoji、各國符號都直接寫進去即可,不需要轉成 中文。但若你的 API 客戶端使用較舊的 Java(< 17)或 Windows 系統,仍可能因為預設編碼錯誤而亂碼。最保險的做法是在 HTTP header 寫死 Content-Type: application/json; charset=utf-8。
import json
data = {"作者": "蘇曉雯", "標題": "五十歲也能逆襲"}
print(json.dumps(data, ensure_ascii=False))
# {"作者": "蘇曉雯", "標題": "五十歲也能逆襲"}
print(json.dumps(data, ensure_ascii=True))
# {"作者": "蘇曉雯", ...}
ensure_ascii=False 才能輸出原始中文,否則 Python 預設會跳脫成 \uXXXX。
2.2 數字精度有上限(IEEE 754 雙精度)
JSON 雖然允許任意精度的數字字面值,但幾乎所有解析器都會把 number 讀進 IEEE 754 64-bit 浮點數,這意味著:
- 整數安全範圍只到
2^53 - 1 = 9007199254740991(JavaScript 的Number.MAX_SAFE_INTEGER) - 浮點運算會有精度誤差,例如
0.1 + 0.2 !== 0.3
如果你的系統會傳超大整數(雪花 ID、區塊鏈金額、64-bit 時間戳),請改用字串:
{
"tweet_id": "1234567890123456789",
"amount_wei": "1000000000000000000"
}
JavaScript 端用 BigInt(str) 轉回大整數,Python 端用 int(str) 即可。
2.3 縮排(pretty print)與壓縮(minify)
JSON 格式化的兩個方向:
- Pretty print:加換行 + 縮排,方便人類閱讀
- Minify:去除所有空白與換行,方便網路傳輸
兩者語意完全等價、只差在 whitespace。慣例:
| 場景 | 建議 |
|---|---|
| API response(生產環境) | Minify(省頻寬) |
| 設定檔、log、文件 | Pretty print(2 space 縮排) |
| Git 追蹤的檔案 | Pretty print + 排序 key(diff 容易看) |
# 用 jq 美化
cat data.json | jq .
# 用 jq 壓縮
cat data.json | jq -c .
# 用 python
python3 -m json.tool data.json
2.4 JSON Pointer(RFC 6901)
當你要在巢狀 JSON 裡引用特定欄位時,用 JSON Pointer 標準路徑:
/users/0/email
對應到:
{
"users": [
{"email": "a@example.com"},
{"email": "b@example.com"}
]
}
語法重點:
/切分階層- 陣列用數字索引(從 0 起)
- key 中本來就含
/要跳脫成~1 - key 中本來就含
~要跳脫成~0
JSON Pointer 廣泛用於 JSON Patch、JSON Schema、OpenAPI 規範。
2.5 JSON Schema 驗證
JSON 本身不帶 schema,但工程化專案多會搭配 JSON Schema 做欄位驗證:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["name", "age"],
"properties": {
"name": {"type": "string", "minLength": 1},
"age": {"type": "integer", "minimum": 0, "maximum": 150},
"email": {"type": "string", "format": "email"}
},
"additionalProperties": false
}
主流語言都有實作(Python jsonschema、JS ajv、Go gojsonschema),可在 API 邊界自動拒絕格式錯誤的請求。
3. 實戰範例
範例 1:解析 GitHub API 回應
import json
import urllib.request
url = "https://api.github.com/repos/python/cpython"
with urllib.request.urlopen(url) as resp:
raw = resp.read().decode("utf-8")
data = json.loads(raw)
print(f"專案:{data['full_name']}")
print(f"星數:{data['stargazers_count']}")
print(f"預設分支:{data['default_branch']}")
預期輸出:
專案:python/cpython
星數:61234
預設分支:main
範例 2:把巢狀 JSON 攤平成 CSV
import json
import csv
raw = '''
[
{"id": 1, "user": {"name": "Alice", "city": "Taipei"}},
{"id": 2, "user": {"name": "Bob", "city": "Tokyo"}}
]
'''
records = json.loads(raw)
with open("out.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["id", "name", "city"])
for r in records:
writer.writerow([r["id"], r["user"]["name"], r["user"]["city"]])
範例 3:JavaScript 安全解析 + 例外處理
function safeParse(text) {
try {
return { ok: true, data: JSON.parse(text) };
} catch (err) {
return { ok: false, error: err.message };
}
}
const result = safeParse('{"foo": 1,}'); // trailing comma 違規
if (!result.ok) {
console.error("解析失敗:", result.error);
}
範例 4:用 jq 抓 nested 欄位
# 抓所有 user 的 email
curl -s https://jsonplaceholder.typicode.com/users | jq '.[].email'
# 過濾年齡 >= 30 的 user 名字
echo '[{"name":"A","age":25},{"name":"B","age":35}]' \
| jq '.[] | select(.age >= 30) | .name'
範例 5:Go 自訂 marshaling
package main
import (
"encoding/json"
"fmt"
"time"
)
type Event struct {
Name string `json:"name"`
At time.Time `json:"at"`
}
func main() {
e := Event{Name: "launch", At: time.Now()}
b, _ := json.MarshalIndent(e, "", " ")
fmt.Println(string(b))
}
json.MarshalIndent 的第三個參數是縮排字串(兩個空白),輸出已 pretty print。
4. 進階技巧
4.1 串流解析(streaming)大檔案
當 JSON 檔案超過 100MB,整個讀進記憶體會 OOM。改用串流:
import ijson
with open("huge.json", "rb") as f:
for item in ijson.items(f, "records.item"):
process(item) # 一次只在記憶體裡保留一筆
JavaScript 端用 stream-json 套件,Java 端用 Jackson JsonParser,都是 SAX-style event-based 解析。
4.2 排序 key 讓 diff 變乾淨
Git 追蹤 JSON 設定檔時,key 順序不穩會導致 diff 充滿假變更。永遠輸出已排序的 key:
json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False)
jq -S . config.json > config.sorted.json
4.3 hash 算 JSON 內容是否相同
兩個語意相同但字串不同的 JSON(key 順序、空白)應該判定相等。先正規化再算 hash:
import hashlib, json
def json_hash(obj):
canonical = json.dumps(obj, sort_keys=True, separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
separators=(",", ":") 去除所有 whitespace,加上 sort_keys=True 後就是 RFC 8785 風格的 canonical form。
4.4 JSON Patch 部分更新
REST API 支援部分更新時,傳遞 JSON Patch(RFC 6902)比整包覆蓋更安全:
[
{ "op": "replace", "path": "/email", "value": "new@example.com" },
{ "op": "add", "path": "/tags/-", "value": "vip" },
{ "op": "remove", "path": "/temp_field" }
]
伺服器收到後用 jsonpatch 套件套用即可。
4.5 NDJSON / JSON Lines
當你要寫 log 或 streaming dataset,一行一個 JSON 物件比包成大陣列更有彈性:
{"ts": "2026-06-05T10:00:00Z", "level": "info", "msg": "start"}
{"ts": "2026-06-05T10:00:01Z", "level": "warn", "msg": "slow"}
副檔名通常為 .ndjson 或 .jsonl。
# 用 jq 處理 NDJSON
cat events.jsonl | jq -c 'select(.level == "warn")'
5. 常見錯誤與解法
錯誤 1:Unexpected token } in JSON at position N
症狀:JS 解析時 throw SyntaxError。
根因:通常是尾隨逗號({"a":1,})或 key 沒加引號。
解法:用線上 lint 工具標出錯誤位置;或在開發階段改用 JSON5 容忍尾隨逗號與註解。
錯誤 2:中文變成 中文
症狀:API 回應中文全變跳脫序列。
根因:序列化時開了 ensure_ascii=True(Python 預設)或 Java 用 Jackson 沒設 WRITE_NUMBERS_AS_STRINGS。
解法:Python 用 json.dumps(..., ensure_ascii=False);Java Jackson 設 mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, false)。
錯誤 3:大整數變成科學記號 / 失去精度
症狀:傳 1234567890123456789 給前端,JS 顯示 1234567890123456800。
根因:JavaScript Number 是 IEEE 754,安全整數上限 2^53 - 1。
解法:API 把這類 ID 改傳字串、或 client 用 JSON.parse(text, (key, value) => ...) 的 reviver 攔截 BigInt 欄位。
錯誤 4:浮點數比較失敗
症狀:JSON 來回反序列化後 0.1 + 0.2 !== 0.3。
根因:所有 JSON 解析器都用 IEEE 754,浮點精度誤差先天存在。
解法:金融金額一律存 cents(整數)或字串;比較時用 epsilon Math.abs(a - b) < 1e-9。
錯誤 5:Pretty print 後變大 4 倍
症狀:API gzip 前後體積差很多。 根因:意外把 minify 改成 indent=2。 解法:生產環境 response 一律 minify;開發環境 / log 才 pretty print。在 Nginx 開 gzip 後差異會大幅縮小,但仍建議 minify。
6. 用 SuperTools 線上版完成相同任務
不想 pip install 或開 jq?打開 SuperTools JSON 格式化工具,貼上 JSON 直接:
- Format:一鍵縮排 2/4 spaces,並 highlight 語法
- Minify:把空白全部去掉,產出最小體積
- Validate:紅字標出 syntax error 行號
- Sort keys:排序所有 key(巢狀也適用)
- JSON ↔ YAML:互轉用於 K8s manifest
工具完全在 client-side 跑(瀏覽器 WebAssembly),你的 JSON 不會上傳到伺服器,可放心貼公司內部 API payload。
實際使用流程:
- 從 Postman 或 Chrome DevTools 複製 response body
- 開 supertools.tw/dev/json-formatter 貼上左側
- 按「Format」右側看美化結果
- 點任一行最左側 ▶ 摺疊節點,快速跳過長陣列
立即使用 SuperTools JSON 格式化工具:https://supertools.tw/dev/json-formatter
7. 其他語言實作
Python(標準庫)
import json
text = json.dumps({"k": "v"}, indent=2, ensure_ascii=False)
obj = json.loads('{"k": "v"}')
JavaScript / TypeScript
const text = JSON.stringify({k: "v"}, null, 2);
const obj = JSON.parse('{"k": "v"}');
Go
b, _ := json.MarshalIndent(map[string]string{"k": "v"}, "", " ")
var obj map[string]string
json.Unmarshal([]byte(`{"k":"v"}`), &obj)
Rust(serde_json)
use serde_json::{json, to_string_pretty};
let v = json!({"k": "v"});
let s = to_string_pretty(&v).unwrap();
Bash(jq)
echo '{"k":"v"}' | jq .
8. FAQ
Q1:JSON 跟 JSON5、JSONC、HJSON 差別?
JSON5 / JSONC 允許註解 + 尾隨逗號,常用在前端設定檔(VSCode settings.json 就是 JSONC)。HJSON 更寬鬆但相容性差。生產 API 一律用標準 JSON。
Q2:JSON 可以放二進位嗎? 不能直接放。請 base64 編碼後當字串塞進去(見 Base64 編碼完整指南)。
Q3:JSON vs Protobuf vs MessagePack? JSON 文字、易讀但體積大;Protobuf / MessagePack 二進位、體積小但需 schema。內部高頻服務改 Protobuf;對外 API 用 JSON 比較友善。
Q4:要支援 JSONP 嗎? 2026 年幾乎不需要。所有現代瀏覽器都支援 CORS,JSONP 已過時且有安全風險。
Q5:JSON 檔案要不要 BOM?
不要。RFC 8259 明確規定 JSON 不應該以 BOM 開頭,雖然解析器多會容忍。寫檔時記得用 utf-8(不是 utf-8-sig)。
9. 實戰案例研究
案例 1:跨服務 ID 精度遺失
某社群 app 後端 Go service 用 int64 ID(雪花算法),前端 React 收到 JSON 解析後最後 3 位數字總是 000。
debug:
- ID 是 1924850384903412345,超過 JS Number.MAX_SAFE_INTEGER (9007199254740991)
JSON.parse強制轉 Number,精度丟失- 後端 log 顯示傳 1924850384903412345,前端收到 1924850384903412000
修法:
- 後端 marshal 時把所有 int64 ID 轉字串:
json:"id,string" - 前端用
BigInt(s)處理或全程當字串 - 跨服務通訊 contract 明確規範
教訓:所有超過 53 bit 的整數,跨 JSON 邊界必須變字串。
案例 2:JSON Schema 救援錯誤 user input
某 SaaS 接 webhook,期望 user 傳特定 schema 的 JSON。某天大量 webhook 噴 500,調查發現 user 改了 schema 欄位型別(從 number 變字串),下游程式 panic。
修法:
- 接收端加 JSON Schema 驗證(
jsonschema套件) - 在 webhook handler 第一行 validate,schema 不符直接 400
- 提供清晰錯誤訊息 + schema doc link
- 加 unit test 涵蓋 5 種典型錯誤 schema
結果:service 從每天 200 次 500 → 0,user 也學會看錯誤訊息修自己的 webhook。
案例 3:大檔 JSON 串流解析
某分析平台每月匯入 5GB 的 JSON 資料(一個大陣列 100 萬筆 record)。原本一次 json.load(f) 吃 12GB RAM 直接 OOM。
改用 ijson 串流:
import ijson
with open("data.json", "rb") as f:
for record in ijson.items(f, "item"):
process(record)
RAM 用量從 12GB → 80MB,處理時間從 OOM crash → 12 分鐘完成。
教訓:JSON 超過 100MB 必用串流解析。
10. 結論 + 延伸閱讀
JSON 看似簡單,但魔鬼藏在編碼、精度、Schema 驗證、串流解析這幾個細節裡。掌握本文 5 個核心觀念 + 5 個實戰範例後,你已經比 80% 的工程師更懂 JSON。下次面對亂碼 payload 時,打開 SuperTools 線上工具 10 秒搞定,把時間留給更值得的事。三條黃金法則:大整數變字串、邊界一定 schema validate、大檔走串流。
延伸閱讀: