
本文介绍一种通用、可扩展的 go 实现方案,通过递归遍历任意深度的嵌套 json(map[string]interface{}),自动将非字符串类型(如数字、布尔值、nil)安全转为字符串,并扁平化为一维字符串切片,最终写入 csv 文件。
本文介绍一种通用、可扩展的 go 实现方案,通过递归遍历任意深度的嵌套 json(map[string]interface{}),自动将非字符串类型(如数字、布尔值、nil)安全转为字符串,并扁平化为一维字符串切片,最终写入 csv 文件。
将嵌套 JSON 转换为 CSV 是常见但易出错的数据处理任务:JSON 支持对象、数组、多层嵌套及混合类型,而 CSV 要求每行是结构一致的字符串序列。Go 中没有开箱即用的“JSON-to-CSV”标准库,需手动解析并做类型适配。核心挑战在于:统一处理嵌套 map[string]interface{}、正确转换 float64(JSON 数字默认解码为此类型)、安全处理 nil 和布尔值,同时避免 panic。
以下是一个生产就绪的实现方案:
package main
import (
"encoding/csv"
"encoding/json"
"fmt"
"log"
"os"
"strconv"
)
// decodeJson 递归遍历 map[string]interface{},将所有叶子值(string/float64/bool/nil)转为字符串,
// 并忽略数组(可按需扩展),返回扁平化的字符串切片
func decodeJson(m map[string]interface{}) []string {
var values []string
for _, v := range m {
switch vv := v.(type) {
case map[string]interface{}:
// 递归处理嵌套对象
values = append(values, decodeJson(vv)...)
case string:
values = append(values, vv)
case float64:
// JSON 数字统一为 float64;保留原始精度(-1 表示最短表示)
values = append(values, strconv.FormatFloat(vv, 'f', -1, 64))
case bool:
values = append(values, strconv.FormatBool(vv))
case nil:
values = append(values, "")
default:
// 兜底:尝试 fmt.Sprint,适用于 int、int64 等(因 json.Unmarshal 可能返回具体整型)
values = append(values, fmt.Sprint(vv))
}
}
return values
}
func main() {
var data interface{}
if err := json.Unmarshal(exampleJSON, &data); err != nil {
log.Fatal("JSON 解析失败:", err)
}
// 确保顶层为 map
root, ok := data.(map[string]interface{})
if !ok {
log.Fatal("JSON 根节点不是对象")
}
// 扁平化为字符串切片
row := decodeJson(root)
// 写入 CSV 文件
f, err := os.Create("output.csv")
if err != nil {
log.Fatal("创建文件失败:", err)
}
defer f.Close()
w := csv.NewWriter(f)
if err := w.Write(row); err != nil {
log.Fatal("写入 CSV 失败:", err)
}
w.Flush()
if err := w.Error(); err != nil {
log.Fatal("刷新 CSV 缓冲区失败:", err)
}
fmt.Printf("✅ 已写入 %d 个字段: %v\n", len(row), row)
// 输出: ✅ 已写入 6 个字段: [Name1 2 somejob somejob2 100 102]
}
var exampleJSON = []byte(`{
"name":"Name1",
"id": 2,
"jobs":{
"job1":"somejob",
"job2":"somejob2"
},
"prevIds":{
"id1": 100,
"id2": 102
}
}`)? 关键设计说明:
- 递归扁平化:遇到 map[string]interface{} 即递归调用,确保任意嵌套层级都被展开;
- 类型安全转换:显式处理 string、float64(JSON 数字)、bool、nil,并增加 default 分支兜底其他数值类型(如 int);
- 空值处理:nil 映射为空字符串 ""(符合 CSV 常见约定),而非 "nil" 字面量;
- CSV 写入健壮性:使用 w.Flush() + w.Error() 检查底层 I/O 错误,避免静默失败。
⚠️ 注意事项与扩展建议:
- 当前版本跳过数组([]interface{}) —— 若 JSON 含数组(如 "tags": ["a","b"]),需决定策略:忽略、转为 JSON 字符串(json.Marshal)、或展开为多行(需生成多条 CSV 记录)。
- 如需支持多行数据(多个 JSON 对象),可将输入改为 []interface{} 切片,对每个对象调用 decodeJson 并逐行 w.Write()。
- 若需保留字段路径(如 jobs.job1)作为 CSV 列名,应改用带路径参数的递归函数,构建键值对映射,再按固定顺序输出 —— 此时需预定义 schema 或动态收集所有键。
该方案兼顾简洁性与可维护性,适用于日志导出、配置快照、ETL 预处理等场景,是 Go 中处理非结构化 JSON → 结构化 CSV 的可靠起点。


















