在数据处理和交换中,JSON和CSV是两种最常用的数据格式。理解它们的差异以及如何在两者之间转换,是每个开发者和数据分析师的必备技能。本指南将全面介绍JSON与CSV转换的所有知识。
目录
核心要点
- 结构差异:JSON支持嵌套和复杂数据结构;CSV是扁平的二维表格格式。
- 数据类型:JSON保留数据类型(字符串、数字、布尔值);CSV中所有数据都是字符串。
- 可读性:CSV更适合表格数据展示;JSON更适合层次化数据。
- 文件大小:CSV通常比等效JSON文件更小。
- 兼容性:CSV可直接在Excel中打开;JSON需要专门的解析器。
- 转换挑战:嵌套JSON转CSV需要扁平化处理,可能丢失结构信息。
需要快速转换JSON和CSV?试试我们的免费在线工具,支持复杂嵌套结构处理。
JSON和CSV格式简介
什么是JSON?
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,基于JavaScript对象语法,但独立于任何编程语言。
JSON示例:
{
"name": "张三",
"age": 28,
"email": "zhangsan@example.com",
"skills": ["JavaScript", "Python", "Go"],
"address": {
"city": "北京",
"district": "朝阳区"
}
}
JSON特点:
- 支持嵌套对象和数组
- 保留数据类型(字符串、数字、布尔值、null)
- 键值对结构
- 广泛用于API数据交换
什么是CSV?
CSV(Comma-Separated Values)是一种简单的表格数据格式,使用逗号分隔字段,换行符分隔记录。
CSV示例:
name,age,email,city
张三,28,zhangsan@example.com,北京
李四,32,lisi@example.com,上海
王五,25,wangwu@example.com,广州
CSV特点:
- 扁平的二维表格结构
- 所有数据都是纯文本
- 可直接在电子表格软件中打开
- 文件体积小,易于传输
JSON与CSV的核心差异
| 特性 | JSON | CSV |
|---|---|---|
| 数据结构 | 层次化、支持嵌套 | 扁平的二维表格 |
| 数据类型 | 字符串、数字、布尔值、null、对象、数组 | 仅字符串 |
| 可读性 | 结构清晰,但较冗长 | 简洁,表格形式直观 |
| 文件大小 | 相对较大(包含键名) | 较小(仅数据和分隔符) |
| 解析复杂度 | 需要JSON解析器 | 简单文本处理即可 |
| 软件兼容性 | 需要专门工具 | Excel、Google Sheets直接支持 |
| 适用场景 | API、配置文件、复杂数据 | 数据导出、报表、批量处理 |
JSON转CSV的原理
将JSON转换为CSV的核心挑战是处理JSON的层次结构,因为CSV只支持二维表格。
扁平化处理
嵌套对象需要通过路径拼接转换为扁平键名:
原始JSON:
{
"user": {
"name": "张三",
"contact": {
"email": "zhangsan@example.com",
"phone": "13800138000"
}
}
}
扁平化后:
{
"user.name": "张三",
"user.contact.email": "zhangsan@example.com",
"user.contact.phone": "13800138000"
}
转换为CSV:
user.name,user.contact.email,user.contact.phone
张三,zhangsan@example.com,13800138000
数组处理策略
数组是JSON转CSV中最复杂的部分,常见处理策略包括:
策略1:数组索引展开
{"tags": ["前端", "后端", "全栈"]}
转换为:
tags.0,tags.1,tags.2
前端,后端,全栈
策略2:数组合并为字符串
tags
"前端,后端,全栈"
策略3:数组展开为多行
tags
前端
后端
全栈
CSV转JSON的原理
CSV转JSON相对简单,主要步骤:
- 解析表头:第一行作为JSON对象的键名
- 解析数据行:每行转换为一个JSON对象
- 类型推断:尝试将字符串转换为适当的数据类型
- 结构重建:根据键名中的分隔符重建嵌套结构
CSV输入:
name,age,city,active
张三,28,北京,true
李四,32,上海,false
JSON输出:
[
{"name": "张三", "age": 28, "city": "北京", "active": true},
{"name": "李四", "age": 32, "city": "上海", "active": false}
]
需要将CSV转换为JSON?使用我们的在线工具:
代码示例
JavaScript
// JSON转CSV
function jsonToCsv(jsonData) {
if (!Array.isArray(jsonData) || jsonData.length === 0) {
return '';
}
const flattenObject = (obj, prefix = '') => {
return Object.keys(obj).reduce((acc, key) => {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
Object.assign(acc, flattenObject(obj[key], newKey));
} else if (Array.isArray(obj[key])) {
acc[newKey] = obj[key].join(';');
} else {
acc[newKey] = obj[key];
}
return acc;
}, {});
};
const flatData = jsonData.map(item => flattenObject(item));
const headers = [...new Set(flatData.flatMap(Object.keys))];
const csvRows = [
headers.join(','),
...flatData.map(row =>
headers.map(header => {
const value = row[header] ?? '';
return typeof value === 'string' && value.includes(',')
? `"${value}"`
: value;
}).join(',')
)
];
return csvRows.join('\n');
}
// CSV转JSON
function csvToJson(csvString) {
const lines = csvString.trim().split('\n');
const headers = lines[0].split(',').map(h => h.trim());
return lines.slice(1).map(line => {
const values = line.split(',');
return headers.reduce((obj, header, index) => {
let value = values[index]?.trim() ?? '';
if (value === 'true') value = true;
else if (value === 'false') value = false;
else if (value !== '' && !isNaN(value)) value = Number(value);
obj[header] = value;
return obj;
}, {});
});
}
// 使用示例
const users = [
{ name: '张三', age: 28, city: '北京' },
{ name: '李四', age: 32, city: '上海' }
];
const csv = jsonToCsv(users);
console.log(csv);
// name,age,city
// 张三,28,北京
// 李四,32,上海
const json = csvToJson(csv);
console.log(json);
Python
import csv
import json
from io import StringIO
from typing import Any
def flatten_dict(d: dict, parent_key: str = '', sep: str = '.') -> dict:
"""扁平化嵌套字典"""
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_dict(v, new_key, sep).items())
elif isinstance(v, list):
items.append((new_key, ';'.join(str(i) for i in v)))
else:
items.append((new_key, v))
return dict(items)
def json_to_csv(json_data: list[dict]) -> str:
"""将JSON数组转换为CSV字符串"""
if not json_data:
return ''
flat_data = [flatten_dict(item) for item in json_data]
all_keys = set()
for item in flat_data:
all_keys.update(item.keys())
headers = sorted(all_keys)
output = StringIO()
writer = csv.DictWriter(output, fieldnames=headers)
writer.writeheader()
writer.writerows(flat_data)
return output.getvalue()
def csv_to_json(csv_string: str) -> list[dict]:
"""将CSV字符串转换为JSON数组"""
reader = csv.DictReader(StringIO(csv_string))
result = []
for row in reader:
parsed_row = {}
for key, value in row.items():
if value.lower() == 'true':
parsed_row[key] = True
elif value.lower() == 'false':
parsed_row[key] = False
elif value.isdigit():
parsed_row[key] = int(value)
else:
try:
parsed_row[key] = float(value)
except ValueError:
parsed_row[key] = value
result.append(parsed_row)
return result
# 使用示例
users = [
{"name": "张三", "age": 28, "address": {"city": "北京", "district": "朝阳区"}},
{"name": "李四", "age": 32, "address": {"city": "上海", "district": "浦东新区"}}
]
csv_output = json_to_csv(users)
print(csv_output)
json_output = csv_to_json(csv_output)
print(json.dumps(json_output, ensure_ascii=False, indent=2))
Go
package main
import (
"encoding/csv"
"encoding/json"
"fmt"
"strconv"
"strings"
)
func flattenMap(data map[string]interface{}, prefix string, result map[string]string) {
for key, value := range data {
newKey := key
if prefix != "" {
newKey = prefix + "." + key
}
switch v := value.(type) {
case map[string]interface{}:
flattenMap(v, newKey, result)
case []interface{}:
var strValues []string
for _, item := range v {
strValues = append(strValues, fmt.Sprintf("%v", item))
}
result[newKey] = strings.Join(strValues, ";")
default:
result[newKey] = fmt.Sprintf("%v", v)
}
}
}
func jsonToCSV(jsonData []map[string]interface{}) string {
if len(jsonData) == 0 {
return ""
}
var flatData []map[string]string
headerSet := make(map[string]bool)
for _, item := range jsonData {
flat := make(map[string]string)
flattenMap(item, "", flat)
flatData = append(flatData, flat)
for k := range flat {
headerSet[k] = true
}
}
var headers []string
for k := range headerSet {
headers = append(headers, k)
}
var sb strings.Builder
writer := csv.NewWriter(&sb)
writer.Write(headers)
for _, row := range flatData {
var values []string
for _, h := range headers {
values = append(values, row[h])
}
writer.Write(values)
}
writer.Flush()
return sb.String()
}
func csvToJSON(csvData string) ([]map[string]interface{}, error) {
reader := csv.NewReader(strings.NewReader(csvData))
records, err := reader.ReadAll()
if err != nil {
return nil, err
}
if len(records) < 2 {
return []map[string]interface{}{}, nil
}
headers := records[0]
var result []map[string]interface{}
for _, row := range records[1:] {
item := make(map[string]interface{})
for i, value := range row {
if i < len(headers) {
if num, err := strconv.ParseFloat(value, 64); err == nil {
item[headers[i]] = num
} else if value == "true" {
item[headers[i]] = true
} else if value == "false" {
item[headers[i]] = false
} else {
item[headers[i]] = value
}
}
}
result = append(result, item)
}
return result, nil
}
func main() {
jsonStr := `[
{"name": "张三", "age": 28, "city": "北京"},
{"name": "李四", "age": 32, "city": "上海"}
]`
var data []map[string]interface{}
json.Unmarshal([]byte(jsonStr), &data)
csvOutput := jsonToCSV(data)
fmt.Println("CSV输出:")
fmt.Println(csvOutput)
jsonOutput, _ := csvToJSON(csvOutput)
jsonBytes, _ := json.MarshalIndent(jsonOutput, "", " ")
fmt.Println("JSON输出:")
fmt.Println(string(jsonBytes))
}
最佳实践
1. 选择正确的转换方向
| 场景 | 推荐格式 |
|---|---|
| API数据交换 | JSON |
| 数据导出到Excel | CSV |
| 配置文件 | JSON |
| 批量数据导入 | CSV |
| 复杂嵌套数据 | JSON |
| 简单表格数据 | CSV |
2. 处理特殊字符
// CSV中处理包含逗号、引号、换行的值
function escapeCSVValue(value) {
if (typeof value !== 'string') return value;
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
}
3. 编码处理
- UTF-8 BOM:导出CSV供Excel打开时,添加BOM(
\uFEFF)确保中文正确显示 - 字符转义:正确处理引号和换行符
// 添加BOM以支持Excel正确显示中文
const csvWithBOM = '\uFEFF' + csvContent;
4. 大数据处理
- 流式处理:处理大文件时使用流式读写,避免内存溢出
- 分批处理:将大数据集分批转换
- 进度反馈:长时间操作时提供进度指示
5. 数据验证
// 转换前验证JSON结构
function validateJsonForCsv(data) {
if (!Array.isArray(data)) {
throw new Error('JSON数据必须是数组');
}
if (data.length === 0) {
throw new Error('JSON数组不能为空');
}
const firstKeys = Object.keys(data[0]).sort().join(',');
for (let i = 1; i < data.length; i++) {
const keys = Object.keys(data[i]).sort().join(',');
if (keys !== firstKeys) {
console.warn(`第${i+1}行的结构与第一行不一致`);
}
}
return true;
}
常见使用场景
-
数据导出
- 将数据库查询结果导出为CSV供业务人员分析
- 生成报表供Excel处理
-
数据导入
- 将Excel数据转换为JSON供API使用
- 批量导入用户数据
-
数据迁移
- 不同系统间的数据格式转换
- 遗留系统数据迁移
-
数据分析
- 将JSON日志转换为CSV进行统计分析
- 准备机器学习训练数据
-
API集成
- 第三方服务数据格式适配
- 多系统数据同步
常见问题
JSON转CSV会丢失数据吗?
转换过程中可能丢失以下信息:
- 数据类型:所有数据变为字符串
- 嵌套结构:被扁平化处理
- 数组顺序:某些处理方式可能改变顺序
建议保留原始JSON作为备份。
如何处理嵌套很深的JSON?
对于深度嵌套的JSON,建议:
- 使用点号路径表示法(如
user.address.city) - 考虑是否真的需要所有嵌套数据
- 可能需要多个CSV文件表示不同层级
CSV中的中文乱码怎么解决?
确保:
- 文件使用UTF-8编码保存
- 导出时添加BOM标记
- Excel打开时选择正确的编码
如何不写代码转换JSON和CSV?
使用在线工具是最快捷的方式:
- JSON转CSV工具 - 支持嵌套JSON扁平化
- CSV转JSON工具 - 支持类型自动推断
大文件转换有什么建议?
- 使用流式处理避免内存问题
- 考虑分批处理
- 使用命令行工具如
jq配合csvkit
总结
JSON和CSV各有优势,选择哪种格式取决于具体使用场景:
选择JSON当:
- 数据有复杂嵌套结构
- 需要保留数据类型
- 用于API数据交换
- 需要灵活的数据结构
选择CSV当:
- 数据是简单的表格形式
- 需要在Excel中处理
- 文件大小是考虑因素
- 需要人工可读性
快速总结:
- JSON支持复杂结构,CSV是扁平表格
- 转换时注意数据类型和嵌套处理
- 特殊字符需要正确转义
- 中文数据注意编码问题
准备好转换数据了吗?试试我们的免费在线工具: