JSON 是 API 的語言,CSV 是試算表、資料庫與分析師的語言。兩者之間的轉換是日常工作 —— 把 API 回應匯出到 Excel,或者把試算表變成 JSON 用於匯入。本指南講解如何在 JavaScript、Python 中把 JSON 轉為 CSV、再把 CSV 轉回 JSON,以及如何在瀏覽器中即時完成;同時涵蓋容易踩雷的邊界情況(巢狀資料、引號、型別)。
核心對映:物件陣列 ⇄ 列
CSV 檔案是一張表:第一列是欄位名稱(標頭),接下來一列一筆紀錄。最自然地對映到這張表的 JSON 形態是一個 物件陣列,每個物件是一列,每個鍵是一欄。
// JSON
[
{ "id": 1, "name": "Ada Lovelace", "active": true },
{ "id": 2, "name": "Bob Khan", "active": false }
]# CSV
id,name,active
1,Ada Lovelace,true
2,Bob Khan,false標頭是所有物件鍵的聯集(這樣欄位不同的物件也能對齊),每一列從每一欄拉取對應的值。
如何在 JavaScript 中把 JSON 轉為 CSV
JavaScript 並沒有內建的 JSON.toCSV,但轉換本身很短。真正要做的只有 跳脫:任何含有逗號、雙引號或換行的值,都必須以雙引號包起來,內部的引號要加倍。
function jsonToCsv(rows) {
if (!rows.length) return '';
const headers = [...new Set(rows.flatMap(Object.keys))];
const escape = (v) => {
if (v == null) return '';
const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
return /[",\n\r]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
};
const lines = [headers.join(',')];
for (const row of rows) {
lines.push(headers.map((h) => escape(row[h])).join(','));
}
return lines.join('\n');
} 留意 typeof v === 'object' 這一分支:巢狀的物件或陣列沒有扁平的 CSV 表達,通行的慣例是把它序列化為 JSON 文字放進儲存格。下面會詳述。
如何在 JavaScript 中把 CSV 轉為 JSON
反過來的雷在於正確解析 CSV —— 欄位可以在引號內包含逗號和換行,所以不能簡單地 split(',')。請使用一個小型狀態機(或在生產環境中改用 papaparse 之類的函式庫):
function csvToJson(text) {
const rows = [];
let row = [], field = '', inQuotes = false;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (inQuotes) {
if (c === '"' && text[i + 1] === '"') { field += '"'; i++; }
else if (c === '"') inQuotes = false;
else field += c;
} else if (c === '"') inQuotes = true;
else if (c === ',') { row.push(field); field = ''; }
else if (c === '\n') { row.push(field); rows.push(row); row = []; field = ''; }
else if (c !== '\r') field += c;
}
if (field || row.length) { row.push(field); rows.push(row); }
const [headers, ...data] = rows;
return data.map((cells) =>
Object.fromEntries(headers.map((h, i) => [h, cells[i] ?? ''])));
}如何在 Python 中互轉 JSON 與 CSV
Python 標準函式庫內建了這項功能 —— 不需要任何依賴。
import csv, json, io
# JSON -> CSV
rows = json.loads(json_text)
buf = io.StringIO()
writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys()))
writer.writeheader()
writer.writerows(rows)
csv_text = buf.getvalue()
# CSV -> JSON
rows = list(csv.DictReader(io.StringIO(csv_text)))
json_text = json.dumps(rows, indent=2)真正重要的邊界情況
巢狀的物件與陣列
CSV 是扁平的,JSON 不是。當值是物件或陣列時,有兩個選擇:在單一儲存格裡把它序列化為 JSON(簡單、可逆),或者扁平化為多欄,例如 address.city、address.zip(更易讀,但有損且更難反向)。若要往返,儲存格裡的 JSON 更安全。
CSV 會遺失型別
CSV 沒有型別 —— 每個欄位都是文字。從 CSV 轉回 JSON 時,除非你刻意把 "1" 強轉為數字,它就是字串。請小心 007 這種值(必須維持字串的郵遞區號/ID),以及當作數字會失去精度的大整數。只在往返無損時才做強轉。
引號與分隔字元
總是要為含逗號、引號或換行的欄位加上引號。某些區域設定(歐洲版 Excel 常見)使用 ; 作為分隔字元 —— 如果你的 CSV 在某一欄看起來不對,請先檢查分隔字元。
鍵不一致
如果你的 JSON 物件不全都擁有相同的鍵,請用所有鍵的聯集來建立標頭,並把缺漏的儲存格留空 —— 不要假設第一個物件就定義了所有欄位。
Excel 與 UTF-8 BOM
如果你的 CSV 會在 Excel 中打開且包含非 ASCII 字元,請在開頭加上 UTF-8 BOM(位元組 0xEF 0xBB 0xBF)。Excel 會把不帶 BOM 的 UTF-8 視為舊式編碼讀取,因而弄亂帶聲調的字母/亞洲字元。多 數 CSV 函式庫都可以選擇輸出 BOM。
// JavaScript —— 寫入 BOM 讓 Excel 正確讀取 UTF-8
const out = '\uFEFF' + jsonToCsv(rows);其他分隔字元(CSV vs TSV vs ;)
「CSV」並不總是以逗號分隔。常見變體:
- TSV —— Tab 分隔;當值經常含逗號時更安全。
- 歐洲版 Excel 在以逗號為小數點的地區使用
;作為分隔字元。
如果打開後輸出看起來擠在奇怪的一欄裡,那就是檔案使用了與讀取方不同的分隔字元。統一一種就好(跨區域 Excel 使用 , + BOM 的 CSV,工程管線使用 TSV)。
面對真實世界的 CSV,請用 PapaParse
在瀏覽器/Node 中解析來自使用者的 CSV,請使用 PapaParse —— 它會處理分隔字元偵測、引號欄位、多行值、標頭,並提供 dynamicTyping 來做數字/布林強轉。上面手寫的片段在可信、簡單的輸入下還行;面對規模請拿出 PapaParse。
線上把 JSON 轉為 CSV —— 不需設定
對於一次性轉換,可以完全跳過程式碼。把 JSON 貼進 fixjson 的 JSON 轉 CSV 工具 並點擊 To CSV,或者貼上 CSV 並點擊 To JSON。它會自動處理引號、鍵的聯集與型別強轉,並完全在你的瀏覽器中執行 —— 不會上傳任何內容,當資料包含客戶紀錄或內部匯出時這點很重要。
常見問題
怎樣把 JSON 轉為 CSV?
拿一個 JSON 物件陣列,以所有鍵的聯集建立標頭,然後為每個物件寫一列 —— 給任何含逗號、引號或換行的值加上引號。Python 用 csv.DictWriter,JavaScript 寫一個小函式,或者直接貼到 線上工具 裡。
怎樣把 CSV 轉為 JSON?
解析 CSV(尊重帶引號的欄位),把第一列視作標頭,把後續每一列對映為以標頭為鍵的物件。請記得:除非你做強轉,否則所有值都是字串。
巢狀的 JSON 物件在 CSV 中怎麼表示?
CSV 無法巢狀,所以巢狀的物件或陣列通常寫成儲存格中的 JSON 文字,或者扁平化為點號欄,例如 address.city。儲存格中的 JSON 是可逆的選項。
可以線上免費把 JSON 轉為 CSV 嗎?
可以 —— fixjson 的轉換器 免費雙向轉換,完全在瀏覽器中執行,不需帳號、不需上傳。
相關工具與指南
- JSON 轉 CSV 工具 —— 在瀏覽器裡雙向轉換
- 如何把 CSV 與 XML 轉為 JSON —— 反方向以及 XML
- 什麼是 JSON? —— 轉換背後的資料型別
- JSON 校驗工具 —— 在轉換前先檢查 JSON 是否可解析