JSON to CSV and Back: A Developer's Complete Guide
· 10 min read
何时使用 JSON 与 CSV
在现代软件开发中,JSON 和 CSV 是两种最常用的数据交换格式。理解它们各自的优势和适用场景对于做出正确的技术决策至关重要。
JSON 的优势
JSON (JavaScript Object Notation) 是一种灵活的数据格式,特别适合以下场景:
- 层次化数据结构:JSON 原生支持嵌套对象和数组,可以表示复杂的数据关系
- API 通信:几乎所有现代 REST API 都使用 JSON 作为默认格式
- 类型保留:JSON 支持字符串、数字、布尔值、null 等多种数据类型
- JavaScript 集成:在 Web 开发中可以直接解析和使用,无需额外转换
- 灵活的模式:不同记录可以有不同的字段,适合动态数据
CSV 的优势
CSV (Comma-Separated Values) 是一种简单的表格数据格式,在以下场景中表现出色:
- 表格数据:适合存储二维数据,如数据库查询结果或电子表格
- Excel 兼容性:可以直接在 Excel、Google Sheets 等工具中打开和编辑
- 文件大小:通常比 JSON 更紧凑,特别是对于大型数据集
- 简单性:格式简单,易于人工阅读和编辑
- 数据分析:大多数数据分析工具和库都原生支持 CSV
何时进行转换
以下是一些常见的转换场景:
- 从 API 获取 JSON 数据后需要在 Excel 中分析
- 将数据库查询结果导出为 CSV 后需要通过 API 传输
- 将复杂的 JSON 配置文件转换为表格格式以便审查
- 将电子表格数据转换为 JSON 以便在 Web 应用中使用
Python 中的 JSON 到 CSV 转换
Python 提供了多种方式来处理 JSON 到 CSV 的转换。我们将探讨两种主要方法:使用 pandas 库和使用标准库的 csv 模块。
使用 Pandas 进行转换
Pandas 是处理数据转换的首选工具,它提供了简洁而强大的 API:
import pandas as pd
import json
# 从文件读取 JSON
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# 转换为 DataFrame
df = pd.DataFrame(data)
# 导出为 CSV
df.to_csv('output.csv', index=False, encoding='utf-8')
# 如果 JSON 是嵌套的,可以使用 json_normalize
from pandas import json_normalize
with open('nested_data.json', 'r', encoding='utf-8') as f:
nested_data = json.load(f)
df = json_normalize(nested_data)
df.to_csv('normalized_output.csv', index=False, encoding='utf-8')
处理 JSON 数组
当 JSON 文件包含对象数组时,pandas 可以轻松处理:
import pandas as pd
# JSON 数组示例
json_data = '''
[
{"name": "张三", "age": 28, "city": "北京"},
{"name": "李四", "age": 32, "city": "上海"},
{"name": "王五", "age": 25, "city": "深圳"}
]
'''
# 直接从 JSON 字符串读取
df = pd.read_json(json_data)
# 或者从 URL 读取
# df = pd.read_json('https://api.example.com/users')
# 导出为 CSV
df.to_csv('users.csv', index=False, encoding='utf-8-sig') # utf-8-sig 用于 Excel 兼容性
使用 CSV 模块进行转换
对于更细粒度的控制,可以使用 Python 的标准 csv 模块:
import json
import csv
# 读取 JSON 文件
with open('data.json', 'r', encoding='utf-8') as json_file:
data = json.load(json_file)
# 确保数据是列表格式
if not isinstance(data, list):
data = [data]
# 获取所有可能的字段名
fieldnames = set()
for item in data:
fieldnames.update(item.keys())
fieldnames = sorted(fieldnames)
# 写入 CSV
with open('output.csv', 'w', newline='', encoding='utf-8') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)
自定义转换逻辑
有时需要在转换过程中进行数据清理或转换:
import json
import csv
from datetime import datetime
def convert_json_to_csv(json_path, csv_path):
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# 数据转换函数
def transform_row(row):
# 转换日期格式
if 'created_at' in row:
row['created_at'] = datetime.fromisoformat(
row['created_at'].replace('Z', '+00:00')
).strftime('%Y-%m-%d %H:%M:%S')
# 转换布尔值
for key, value in row.items():
if isinstance(value, bool):
row[key] = 'Yes' if value else 'No'
return row
# 应用转换
transformed_data = [transform_row(row.copy()) for row in data]
# 写入 CSV
if transformed_data:
fieldnames = transformed_data[0].keys()
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(transformed_data)
convert_json_to_csv('input.json', 'output.csv')
JavaScript/Node.js 中的 JSON 到 CSV 转换
在 JavaScript 和 Node.js 环境中,有多种方法可以实现 JSON 到 CSV 的转换。
使用 json2csv 库
json2csv 是 Node.js 中最流行的转换库:
const { Parser } = require('json2csv');
const fs = require('fs');
// 示例数据
const data = [
{ name: '张三', age: 28, city: '北京', salary: 50000 },
{ name: '李四', age: 32, city: '上海', salary: 60000 },
{ name: '王五', age: 25, city: '深圳', salary: 55000 }
];
// 配置字段
const fields = ['name', 'age', 'city', 'salary'];
const opts = { fields };
try {
const parser = new Parser(opts);
const csv = parser.parse(data);
// 写入文件
fs.writeFileSync('output.csv', csv, 'utf8');
console.log('CSV 文件已生成');
} catch (err) {
console.error('转换错误:', err);
}
自定义字段和格式化
json2csv 支持高级配置选项:
const { Parser } = require('json2csv');
const fs = require('fs');
const data = [
{
name: '张三',
age: 28,
salary: 50000,
joinDate: '2020-01-15',
active: true
},
{
name: '李四',
age: 32,
salary: 60000,
joinDate: '2019-06-20',
active: false
}
];
// 自定义字段配置
const fields = [
{ label: '姓名', value: 'name' },
{ label: '年龄', value: 'age' },
{
label: '月薪',
value: (row) => `¥${row.salary.toLocaleString()}`
},
{
label: '入职日期',
value: 'joinDate',
default: 'N/A'
},
{
label: '状态',
value: (row) => row.active ? '在职' : '离职'
}
];
const opts = {
fields,
quote: '"',
delimiter: ',',
header: true
};
const parser = new Parser(opts);
const csv = parser.parse(data);
fs.writeFileSync('formatted_output.csv', csv, 'utf8');
浏览器端转换
在浏览器中,可以使用纯 JavaScript 实现转换:
function jsonToCsv(jsonData) {
if (!jsonData || jsonData.length === 0) {
return '';
}
// 获取所有键作为表头
const headers = Object.keys(jsonData[0]);
// 转义 CSV 值
const escapeCsvValue = (value) => {
if (value === null || value === undefined) {
return '';
}
const stringValue = String(value);
if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
return `"${stringValue.replace(/"/g, '""')}"`;
}
return stringValue;
};
// 构建 CSV 字符串
const csvRows = [];
// 添加表头
csvRows.push(headers.map(escapeCsvValue).join(','));
// 添加数据行
for (const row of jsonData) {
const values = headers.map(header => escapeCsvValue(row[header]));
csvRows.push(values.join(','));
}
return csvRows.join('\n');
}
// 下载 CSV 文件
function downloadCsv(jsonData, filename = 'data.csv') {
const csv = jsonToCsv(jsonData);
const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
// 使用示例
const data = [
{ name: '张三', age: 28, city: '北京' },
{ name: '李四', age: 32, city: '上海' }
];
downloadCsv(data, 'users.csv');
使用 Stream 处理大文件
对于大型 JSON 文件,使用流式处理可以避免内存问题:
const { Transform } = require('stream');
const fs = require('fs');
const JSONStream = require('JSONStream');
// 创建转换流
class JsonToCsvTransform extends Transform {
constructor(options = {}) {
super({ objectMode: true });
this.headers = options.headers || [];
this.headerWritten = false;
}
_transform(chunk, encoding, callback) {
try {
// 写入表头
if (!this.headerWritten) {
if (this.headers.length === 0) {
this.headers = Object.keys(chunk);
}
this.push(this.headers.join(',') + '\n');
this.headerWritten = true;
}
// 写入数据行
const values = this.headers.map(header => {
const value = chunk[header];
if (value === null || value === undefined) return '';
const str = String(value);
return str.includes(',') ? `"${str.replace(/"/g, '""')}"` : str;
});
this.push(values.join(',') + '\n');
callback();
} catch (err) {
callback(err);
}
}
}
// 使用流式转换
fs.createReadStream('large_data.json')
.pipe(JSONStream.parse('*'))
.pipe(new JsonToCsvTransform())
.pipe(fs.createWriteStream('output.csv'))
.on('finish', () => console.log('转换完成'))
.on('error', (err) => console.error('转换错误:', err));
CSV 到 JSON 的转换
反向转换同样重要,特别是当需要将电子表格数据导入到 Web 应用或 API 时。
Python 中的 CSV 到 JSON
使用 pandas 进行转换非常简单:
import pandas as pd
# 读取 CSV
df = pd.read_csv('input.csv', encoding='utf-8')
# 转换为 JSON
# 方式 1: 记录列表格式
json_records = df.to_json(orient='records', force_ascii=False, indent=2)
# 方式 2: 列导向格式
json_columns = df.to_json(orient='columns', force_ascii=False, indent=2)
# 方式 3: 索引导向格式
json_index = df.to_json(orient='index', force_ascii=False, indent=2)
# 写入文件
with open('output.json', 'w', encoding='utf-8') as f:
f.write(json_records)
# 或者直接写入
df.to_json('output.json', orient='records', force_ascii=False, indent=2)
使用 CSV 模块转换
对于更精细的控制,可以使用标准库:
import csv
import json
def csv_to_json(csv_path, json_path):
data = []
with open(csv_path, 'r', encoding='utf-8') as csv_file:
csv_reader = csv.DictReader(csv_file)
for row in csv_reader:
# 数据类型转换
processed_row = {}
for key, value in row.items():
# 尝试转换为数字
try:
if '.' in value:
processed_row[key] = float(value)
else:
processed_row[key] = int(value)
except ValueError:
# 保持为字符串
processed_row[key] = value
data.append(processed_row)
with open(json_path, 'w', encoding='utf-8') as json_file:
json.dump(data, json_file, ensure_ascii=False, indent=2)
csv_to_json('input.csv', 'output.json')
Node.js 中的 CSV 到 JSON
使用 csv-parser 库可以轻松实现:
const fs = require('fs');
const csv = require('csv-parser');
const results = [];
fs.createReadStream('input.csv')
.pipe(csv())
.on('data', (data) => {
// 数据类型转换
const processed = {};
for (const [key, value] of Object.entries(data)) {
// 尝试转换数字
const num = Number(value);
processed[key] = !isNaN(num) && value !== '' ? num : value;
}
results.push(processed);
})
.on('end', () => {
fs.writeFileSync('output.json', JSON.stringify(results, null, 2), 'utf8');
console.log('CSV 已成功转换为 JSON');
})
.on('error', (err) => {
console.error('转换错误:', err);
});
浏览器端 CSV 到 JSON
在浏览器中处理用户上传的 CSV 文件:
function csvToJson(csvText) {
const lines = csvText.split('\n').filter(line => line.trim());
if (lines.length === 0) return [];
const headers = lines[0].split(',').map(h => h.trim());
const result = [];
for (let i = 1; i < lines.length; i++) {
const obj = {};
const currentLine = lines[i].split(',');
headers.forEach((header, index) => {
let value = currentLine[index] ? currentLine[index].trim() : '';
// 移除引号
if (value.startsWith('"') && value.endsWith('"')) {
value = value.slice(1, -1).replace(/""/g, '"');
}
// 尝试转换为数字
const num = Number(value);
obj[header] = !isNaN(num) && value !== '' ? num : value;
});
result.push(obj);
}
return result;
}
// 处理文件上传
document.getElementById('csvFile').addEventListener('change', (event) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
const csvText = e.target.result;
const jsonData = csvToJson(csvText);
console.log('转换后的 JSON:', jsonData);
// 下载 JSON 文件
const blob = new Blob([JSON.stringify(jsonData, null, 2)],
{ type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'output.json';
a.click();
};
reader.readAsText(file);
});
处理嵌套的 JSON 对象
嵌套的 JSON 结构是转换为 CSV 时最具挑战性的问题之一。CSV 是扁平的表格格式,而 JSON 可以有任意深度的嵌套。
扁平化策略
有几种常见的扁平化策略:
- 点符号扁平化:使用点号连接嵌套键,如
user.address.city - 下划线扁平化:使用下划线连接,如
user_address_city - JSON 字符串化:将嵌套对象转换为 JSON 字符串存储在单个列中
- 多表分离:将嵌套数据拆分为多个 CSV 文件
Python 中的扁平化
使用 pandas 的 json_normalize 函数:
import pandas as pd
from pandas import json_normalize
# 嵌套 JSON 示例
nested_data = [
{
"id": 1,
"name": "张三",
"address": {
"city": "北京",
"district": "朝阳区",
"street": "建国路"
},
"contacts": {
"email": "[email protected]",
"phone": "13800138000"
}
},
{
"id": 2,
"name": "李四",
"address": {
"city": "上海",
"district": "浦东新区",
"street": "世纪大道"
},
"contacts": {
"email": "[email protected]",
"phone": "13900139000"
}
}
]
# 扁平化嵌套结构
df = json_normalize(nested_data, sep='_')
print(df.columns)
# 输出: ['id', 'name', 'address_city', 'address_district', 'address_street',
# 'contacts_email', 'contacts_phone']
df.to_csv('flattened.csv', index=False, encoding='utf-8')
自定义扁平化函数
对于更复杂的场景,可以编写自定义扁平化逻辑:
def flatten_json(nested_json, separator='_', prefix=''):
"""
递归扁平化嵌套的 JSON 对象
"""
flattened = {}
for key, value in nested_json.items():
new_key = f"{prefix}{separator}{key}" if prefix else key
if isinstance(value, dict):
# 递归处理嵌套字典
flattened.update(flatten_json(value, separator, new_key))
elif isinstance(value, list):
# 处理列表:转换为 JSON 字符串或展开
if len(value) > 0 and isinstance(value[0], dict):
# 如果是对象列表,转换为 JSON 字符串
import json
flattened[new_key] = json.dumps(value, ensure_ascii=False)
else:
# 如果是简单值列表,用分号连接
flattened[new_key] = ';'.join(map(str, value))
else:
flattened[new_key] = value
return flattened
# 使用示例
nested_data = {
"user": {
"id": 1,
"profile": {
"name": "张三",
"age": 28
},
"settings": {
"theme": "dark",
"notifications": {
"email": True,
"sms": False
}
}
}
}
flattened = flatten_json(nested_data)
print(flattened)
# 输出: {
# 'user_id': 1,
# 'user_profile_name': '张三',
# 'user_profile_age': 28,
# 'user_settings_theme': 'dark',
# 'user_settings_notifications_email': True,
# 'user_settings_notifications_sms': False
# }
JavaScript 中的扁平化
在 Node.js 中实现类似的扁平化逻辑:
function flattenObject(obj, prefix = '', separator = '_') {
const flattened = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = prefix ? `${prefix}${separator}${key}` : key;
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
// 递归处理嵌套对象
Object.assign(flattened, flattenObject(value, newKey, separator));
} else if (Array.isArray(value)) {
// 处理数组
if (value.length > 0 && typeof value[0] === 'object') {
flattened[newKey] = JSON.stringify(value);
} else {
flattened[newKey] = value.join(';');
}
} else {
flattened[newKey] = value;
}
}
return flattened;
}
// 使用 json2csv 与扁平化
const { Parser } = require('json2csv');
const fs = require('fs');
const nestedData = [
{
id: 1,
user: {
name: '张三',
contact: {
email: '[email protected]',
phone: '13800138000'
}
},
metadata: {
created: '2024-01-01',
updated: '2024-01-15'
}
}
];
// 扁平化所有记录
const flattenedData = nestedData.map(item => flattenObject(item));
const parser = new Parser();
const csv = parser.parse(flattenedData);
fs.writeFileSync('flattened_output.csv', csv, 'utf8');
处理深度嵌套的最佳实践
对于非常深的嵌套结构,考虑以下策略:
import pandas as pd
import json
def smart_flatten(data, max_depth=3):
"""
智能扁平化:限制深度,超过深度的转为 JSON 字符串
"""
def flatten_recursive(obj, prefix='', depth=0):
flattened = {}
if depth >= max_depth:
# 超过最大深度,转为 JSON 字符串
return {prefix.rstrip('_'): json.dumps(obj, ensure_ascii=False)}
for key, value in obj.items():
new_key = f"{prefix}{key}"
if isinstance(value, dict):
flattened.update(flatten_recursive(value, f"{new_key}_", depth + 1))
else:
flattened[new_key] = value
return flattened
if isinstance(data, list):
return [flatten_recursive(item) for item in data]
else:
return flatten_recursive(data)
# 使用示例
deep_nested = [
{
"level1": {
"level2": {
"level3": {
"level4": {
"value": "too deep"
}
}
}
}
}
]
result = smart_flatten(deep_nested, max_depth=2)
df = pd.DataFrame(result)
df.to_csv('smart_flattened.csv', index=False)
处理 JSON 中的数组
数组是 JSON 中常见的结构,但在 CSV 中表示数组需要特殊处理。
数组处理策略
有几种方法可以处理 JSON 数组:
- 连接为字符串:使用分隔符(如分号或竖线)连接数组元素
- 创建多行:为数组的每个元素创建一行,重复父级数据
- 创建多列:为数组的每个索引创建一列
- JSON 字符串化:将整个数组转换为 JSON 字符串
Python 中处理数组
import pandas as pd
from pandas
Frequently Asked Questions
How do I convert JSON to CSV in Python?
Use pandas: import pandas as pd; df = pd.json_normalize(data); df.to_csv("output.csv", index=False). For simple JSON, the csv module works too.
How do I handle nested JSON when converting to CSV?
Flatten nested objects using dot notation (e.g., address.city becomes a column). pandas json_normalize handles this automatically. For deeply nested data, write a recursive flattener.
What is the best command-line tool for JSON to CSV?
jq combined with csvkit: jq -r ".[] | [.field1, .field2] | @csv" input.json > output.csv. Miller (mlr) is also excellent for format conversion.
Can I convert CSV back to JSON?
Yes. In Python: pd.read_csv("file.csv").to_json(orient="records"). In JavaScript: use csv-parse or papaparse library.
How do I handle large JSON files that do not fit in memory?
Use streaming parsers like ijson (Python) or JSONStream (Node.js) to process records one at a time, writing CSV rows incrementally.
Related Tools