跳至內容

JSON 轉 CSV:攤平巢狀資料

如何將巢狀 JSON 轉成扁平 CSV 列——點分鍵、陣列處理、跳脫、編碼。含實際範例與資訊損失的因應策略。

CSV 是資料分析工具的通用貨幣——Excel、Google Sheets、所有資料庫匯入、 所有 BI 工具。JSON 則是樹狀結構。兩者之間的轉換不是 1 對 1:攤平的 過程會做出抉擇、會損失資訊。本文整理你必須明確做的選擇,免得在下游 給自己驚喜。

為何 CSV 需要扁平列

CSV 是 2D 格式:一列 header 加 N 列資料,欄位數固定。JSON 是 N 維—— 物件巢狀、陣列巢狀於物件、物件巢狀於陣列。任何轉換都需要把樹 投影 成表格。

三個動作完成投影:

  1. 選定「列實體」——通常是頂層陣列的元素。
  2. 將巢狀物件以點分路徑攤平成欄位。
  3. 決定如何處理巢狀陣列——展開(explode)、串接(join)、或索引(index)。

物件陣列對應到列

最簡單的情況。JSON 是扁平陣列、每個元素也扁平:

[
  { "id": 1, "name": "Ada", "active": true },
  { "id": 2, "name": "Alan", "active": false }
]

每個鍵成為一欄、每個元素成為一列:

id,name,active
1,Ada,true
2,Alan,false

header 是所有元素鍵的 聯集。若某列沒有某鍵,該格留空。

攤平巢狀物件(點分鍵)

巢一層、加一個點:

[
  {
    "id": 1,
    "name": "Ada",
    "address": { "city": "London", "zip": "SW1A 2AA" }
  }
]
id,name,address.city,address.zip
1,Ada,London,SW1A 2AA

多層繼續點下去:address.country.code。這套表示法與 JSONPath 一致,欄名能直接看出 來源。有的工具用 address_city(底線)或 address__city(雙底線), 擇一並寫進文件即可。

紀錄內的陣列

轉換在此變得有意見。三種合理策略,各自有不同的資訊損失:

策略 A:展開(explode)。 一個陣列元素一列。當陣列元素才是真正 的紀錄時最適合:

[
  { "id": 1, "tags": ["admin", "founder"] }
]
id,tag
1,admin
1,founder

這就是 jq[.id, .tags[]] | @csv 做的事,也是 SQL 的 UNNEST。 兩個缺點——你失去原本「一列一使用者」的結構,且若紀錄有多個陣列, 你得展開笛卡爾乘積或擇一展開。

策略 B:串接(join)。 將陣列編成單一定界字串:

id,tags
1,"admin;founder"

適合短陣列、且消費端能再切分。請選用值內不會出現的定界字元, 通常是分號或豎線。

策略 C:索引(index)。 每個陣列位置一欄:

id,tags.0,tags.1
1,admin,founder

適合固定短長度且位置有意義(如 RGB 三元組)。長度可變時會崩—— 最後 tags.0tags.99 大半空著。

沒有萬用解。請依消費端用途選擇。

Header、定界、跳脫、編碼

四個在生產環境會咬人的 CSV 變數:

  • 定界字元。RFC 4180 規定逗號,歐洲 Excel 用分號,TSV 用 tab。請在你的轉換器明確設定並寫進文件。
  • 跳脫與引號。值若含定界字元、換行或雙引號,必須加引號。引號字元是 ",引號內的 " 以雙寫處理:"。所以 she said "hi" 變成 "she said ""hi"""
  • Header。RFC 4180 中是可選,但你應該永遠輸出。沒有 header 的轉換對下游是黑盒。
  • 編碼。UTF-8 是唯一安全答案。Windows 版 Excel 雙擊 .csv 仍會用區域 8-bit 編碼——在檔頭加 UTF-8 BOM(EF BB BF)能讓 Excel 自動辨識 UTF-8。沒錯,這正是 JSON 語法錯誤 告訴你要在 JSON 避免的 BOM;CSV 對它友善。

有損轉換與緩解

會丟資訊的轉換:

  • 型別流失——CSV 沒有型別。"true"true1 都會變成字串 true。請固定約定:數字用十進位、布林用 true/false1/0、日期用 ISO 8601。
  • null vs 空字串——CSV 沒有 null。多數工具用空字串代替。若需區分「缺值」與「空字串」,請用不同編碼(例如 PostgreSQL COPY\N)。
  • 巢狀陣列順序——展開時每列內部順序保留,但消費端不一定能追回父紀錄,除非你帶一欄 id。
  • 陣列內物件身份——展開含物件的陣列會讓欄位倍增:orders[*].idorders[*].total 需要加索引後綴或改成多列展開。

緩解方式都一樣:寫下轉換約定,並當作資料合約的一部分。

處理大型輸入

若 JSON 很大,不要整批載入再轉。請參考 處理大型 JSON 檔案 的串流模式——串流 JSON 解析器接到串流 CSV writer,可在常數記憶體 下處理任意大小的輸入。

動手轉

把 JSON 貼進 /zh-Hant/json/convert/csv—— 提供策略 A、B、C 處理巢狀陣列、可選定界字元、可切換 UTF-8 BOM 以 配合 Excel。全部在瀏覽器內執行,資料不會離開你的裝置。

延伸閱讀