JSON 是 API 通信的事实标准,但数据分析师和 Excel 用户更习惯 CSV。今天我们用 OpenCode 花 4 分钟写一个命令行 JSON 转 CSV 转换器,支持嵌套 JSON 自动扁平化、从标准输入和文件读取、自定义分隔符,代码 100 行出头,只用 Python 标准库。
技术栈:Python 3 + argparse + json + csv
我对 OpenCode 说:
> 用 Python 写一个命令行 JSON 转 CSV 工具,接收一个 JSON 文件路径作为参数,将 JSON 数组转为 CSV 输出到 stdout。用 argparse 解析参数。
OpenCode 秒级生成了核心代码:
import json
import csv
import sys
import argparse
def main():
parser = argparse.ArgumentParser(description="JSON 转 CSV 转换器")
parser.add_argument("file", help="JSON 文件路径")
args = parser.parse_args()
with open(args.file, "r", encoding="utf-8") as f:
data = json.load(f)
writer = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
要点:OpenCode 自动选择了 csv.DictWriter——它比我手动拼逗号靠谱多了,还能正确处理引号转义。
我对 OpenCode 说:
> 加上从 stdin 读取的功能,如果文件参数是 "-" 或不传,就从 stdin 读。另外 JSON 数组里如果有嵌套对象,自动扁平化——用点号连接嵌套键,比如 {"a": {"b": 1}} 变成列 "a.b"。
OpenCode 的响应:
def flatten(d, parent_key="", sep="."):
items = []
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.extend(flatten(v, new_key, sep=sep).items())
elif isinstance(v, list):
items.append((new_key, json.dumps(v, ensure_ascii=False)))
else:
items.append((new_key, v))
return dict(items)
要点:扁平化是这类工具的隐藏难点。直接告诉 OpenCode"用点号连接嵌套键",它就生成了递归展平函数。列表类型它自动序列化为 JSON 字符串——这是合理取舍。
我对 OpenCode 说:
> 现在如果数组里每个对象的键不一样,csv.DictWriter 会报错。需要先扫描所有行收集全部列名再输出。再加一个 -d 参数自定义分隔符,默认逗号。
OpenCode 修改后:
def collect_keys(data):
keys = {}
for row in data:
flat = flatten(row)
for k in flat:
keys[k] = None
return keys
要点:这个修改体现了"用自然语言描述边界条件"的思路。我不需要写具体的 key 收集逻辑,只需要描述问题:"如果每个对象键不一样会报错",OpenCode 就会补齐实现。
我对 OpenCode 说:
> 帮我写一条测试命令,生成一份嵌套 JSON 并用管道传给脚本。
OpenCode 给出了测试命令,我跑了一下:
echo '[{"name":"张三","info":{"age":28,"city":"北京"},"tags":["go","python"]},{"name":"李四","info":{"age":32,"city":"上海"},"tags":["rust","js"]}]' | python json2csv.py - -d $'\t'
输出:
name info.age info.city tags 张三 28 北京 ["go", "python"] 李四 32 上海 ["rust", "js"]
#!/usr/bin/env python3
"""JSON 转 CSV 格式转换器 - 支持嵌套扁平化"""
import json
import csv
import sys
import argparse
def flatten(d, parent_key="", sep="."):
items = []
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.extend(flatten(v, new_key, sep=sep).items())
elif isinstance(v, list):
items.append((new_key, json.dumps(v, ensure_ascii=False)))
else:
items.append((new_key, v))
return dict(items)
def collect_keys(data):
keys = {}
for row in data:
flat = flatten(row)
for k in flat:
keys[k] = None
return keys
def main():
parser = argparse.ArgumentParser(description="JSON 转 CSV 格式转换器")
parser.add_argument("file", nargs="?", default="-", help="JSON 文件路径(默认从 stdin 读取)")
parser.add_argument("-d", "--delimiter", default=",", help="CSV 分隔符(默认逗号)")
args = parser.parse_args()
if args.file == "-":
data = json.load(sys.stdin)
else:
with open(args.file, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict):
data = [data]
fieldnames = collect_keys(data)
writer = csv.DictWriter(
sys.stdout, fieldnames=fieldnames, delimiter=args.delimiter
)
writer.writeheader()
for row in data:
writer.writerow(flatten(row))
if __name__ == "__main__":
main()
从零到可用只花了 4 分钟。整段对话我只需要描述"我想要什么",不需要纠结 csv.DictWriter 的参数顺序、不需要手动处理嵌套字典的递归,也不需要写 key 收集的 boilerplate。
效率对比:如果手写,查文档、调试嵌套、处理边界情况至少 30 分钟。OpenCode 把"实现细节"完全交给了 AI,我只负责"我想要的效果是什么"——这才是 AI 编程助手的正确打开方式。