大多数 CSV 文件都是混乱的。它们在应该为空的地方有空白单元格,引号不一致,且没有强制的模式。将这些数据转换为干净的 JSON 并不是格式上的变化,而是一个数据清洗问题。
本指南将介绍何时进行转换,转换过程中可能出现的问题,以及如何正确处理边缘情况——无论是使用一个快速 CSV 转 JSON 转换器 还是自定义脚本。
CSV 与 JSON:真实的权衡
CSV 紧凑、普遍可读,并且可以轻松导入到电子表格中。对于扁平的表格数据——来自数据库、分析管道、财务记录的导出——它仍然是正确的格式。如果您的数据仅以行和列的形式存在,且没有嵌套结构,那么 CSV 是合适的。
当出现以下情况时,JSON 成为更好的选择:
- 您的数据具有嵌套或层次结构(例如一个用户拥有多个地址)
- 您需要明确的数据类型信息(例如数字 42 与字符串 “42”)
- 您正在将数据提供给 API 或 JavaScript 应用程序
- 您希望明确表示 null,而不是仅仅用空单元格来表示
这种权衡并不是关于哪种格式“更好”。而是关于您的数据结构是否超出了 CSV 能够表示的范围。
CSV 引发的问题
在转换之前,您需要理解其中存在的问题。
没有关于 null 的标准。 CSV 中的空单元格可能表示 null、零、空字符串或缺失数据。从文件中无法判断其具体含义。一个空列在某个系统中可能表示“未知”,而在另一个系统中可能表示“0”。 age 引号不一致。
包含逗号的值应被双引号包裹( ),但并非所有导出工具都遵循此规范。您会发现未加引号的逗号、引号不匹配以及单元格打开引号却未关闭的情况。"Portland, OR"编码问题。
CSV 文件通常带有 UTF-8 BOM、Windows-1252 字符或两者混合。智能引号、em 破折号和带重音符号的字符都会导致期望 ASCII 清洁格式的解析器出错。 没有类型信息。
CSV 中的每个值都是字符串。数字 42、布尔值 true 和日期 2024-01-01 都被存储为文本。盲目地转换为 JSON 会得到一个充满字符串的文档,而您期望的是数字和布尔值。 如何正确转换
其机制很简单:每一行 CSV 转换为一个 JSON 对象,列标题作为键。问题在于转换前后发生的所有操作。
类型转换。
包含整数的列应转换为 JSON 数字。包含“true”/“false”值的列应转换为布尔值。但像 ZIP 代码这样的列,即使看起来像整数(如 90210),也应保持为字符串——转换会丢失前导零。 处理 null 值。
空单元格需要做出决定:转换为 ,跳过该键,或使用默认值。选择一个方案并保持一致。 null缺失字段。
如果某一行的列数少于标题行,您的解析器需要优雅地处理这种情况——要么填充为 ,要么跳过该行。 null 以下是一个 Python 代码片段,可以处理所有三种情况:
编码会移除文件开头的 BOM。
import csv
import json
def coerce_value(value):
if value == '':
return None
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() in ('true', 'false'):
return value.lower() == 'true'
return value
def csv_to_json(csv_path, json_path):
with open(csv_path, encoding='utf-8-sig') as f: # utf-8-sig strips BOM
reader = csv.DictReader(f)
rows = []
for row in reader:
rows.append({k: coerce_value(v) for k, v in row.items() if k})
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(rows, f, indent=2, ensure_ascii=False)
csv_to_json('input.csv', 'output.json')
这 utf-8-sig 尝试按顺序进行数值转换:先整数,再浮点数,再布尔值,最后字符串。该 coerce_value 过滤器会移除标题行中尾随逗号导致的多余列。 if k 从扁平 CSV 生成嵌套 JSON
CSV 无法直接表示嵌套结构,但有两种常见策略。
点键表示法。
某些 CSV 导出使用类似 的标题。一个后处理步骤会按点分割并构建嵌套对象: address.city 且 address.zip分组与嵌套。
def unflatten(row):
result = {}
for key, value in row.items():
parts = key.split('.')
d = result
for part in parts[:-1]:
d = d.setdefault(part, {})
d[parts[-1]] = value
return result
如果多行代表同一个父级的子项(例如订单下的订单行),则按父级 ID 分组,并在转换后构建嵌套数组。这两种方法都不应出现在快速转换步骤中——如果您正在这样做,说明您正在编写一个转换脚本,而不仅仅是更改格式。 选择使用哪种工具
对于一个结构良好、仅需格式转换的干净 CSV 文件:使用在线
。粘贴,转换,完成。无需设置,无需依赖项。 CSV 转 JSON 转换器对于已知存在问题的 CSV 文件——编码问题、引号不一致、类型转换或 null 值——编写一个脚本。上述 Python 代码片段可以处理大多数现实世界的情况。pandas 也是一个选项(
),但它会做出自己的类型转换决策,可能不符合您的期望。pd.read_csv() + df.to_json()对于终端中的单行命令,
Miller ) 是最快的方法: (mlrMiller 能够正确处理引号、编码和缺失字段,无需编写任何代码即可从有问题的 CSV 转换为干净的 JSON。
mlr --icsv --ojson cat input.csv > output.json
CSV 的边缘情况及如何处理
CSV 问题
| JSON 结果 | 如何处理 | 空单元格 |
|---|---|---|
| 或缺失键 | null 提前决定;在所有行中保持一致 | 看起来像数字的字符串(如 ZIP 代码) |
| 如果进行类型转换会丢失前导零 | 保持为字符串;仅对您控制的列进行类型转换 | 标题行中的尾随逗号 |
| 空键 | 在每一行中 "" 在构建对象时使用 | 文件开头的 UTF-8 BOM if k 解析错误或 |
| 在第一个键中 | 以 打开 | 单元格内的引号换行 encoding='utf-8-sig' |
| 会破坏简单的逐行解析器 | 使用真正的 CSV 解析器,而不是 | 在布尔列中 split(',') |
"true"/"false" 字符串,而不是 | "true" 布尔值 true 在明确比较后进行显式转换 | 格式转换很容易,但数据本身却不是。 |
CSV 转 JSON 转换器可以在几秒钟内完成结构转换。真正耗时的是理解文件中实际存在的内容——null 值、编码问题、看起来像数字但实际不是的列。明确编写类型转换和 null 处理逻辑,而不是依赖工具猜测,这样可以避免因数据错误而引发的下游问题。
CSV 转 JSON:何时转换以及如何保持数据清洁 2
