Skip to content
Назад к блогу
Руководства

CSV в JSON: способы, ловушки и примеры кода

Конвертируем CSV в JSON (и обратно) с Python, JavaScript и CLI-инструментами. Кодировки, приведение типов и стриминг больших файлов.

12 мин чтения

Руководство по конвертации CSV ↔ JSON: способы, ловушки и лучшие практики

Команда эксплуатации присылает вам CSV-экспорт. Ваш API ожидает JSON. Вы открываете файл, смотрите на 10 000 строк значений через запятую и думаете: какой самый быстрый способ конвертировать CSV в JSON, не теряя данные?

Это руководство охватывает четыре способа конвертации (браузерные инструменты, JavaScript, Python, CLI), обратное направление (JSON в CSV), пять ловушек, тихо портящих данные, и подход к файлам, не помещающимся в память.

CSV против JSON: когда какой формат

Перед конвертацией полезно понимать сильные стороны каждого формата.

ПараметрCSVJSON
СтруктураПлоская таблица (строки и колонки)Вложенная иерархия (объекты, массивы)
Типы данныхВсё — строкиstring, number, boolean, null
ЧитаемостьУдобен в таблицахУдобен разработчику
ПрименениеЭкспорт/импорт данных, отчёты, ETLAPI, конфиги, NoSQL-хранилище
Размер файлаМеньше (нет повторяющихся ключей)Больше (ключи повторяются в каждой записи)
СхемаНеявная (строка заголовка)Явная (или JSON Schema)

Правило большого пальца: применяйте CSV, когда данные табличные, а потребитель — таблица или конвейер данных. Применяйте JSON, когда данные иерархичны или потребитель — API. Структуру JSON-вывода всегда можно проверить в форматировщике JSON, чтобы поймать структурные проблемы заранее.

Если ваш проект использует расширенные форматы JSON5 или JSONC для конфигурации, см. руководство по JSON5 и JSONC для синтаксических различий и инструментов.

Четыре способа конвертации CSV в JSON

Способ 1 — браузерный инструмент

Для разовых конвертаций браузерный подход — самый быстрый путь. Вставьте CSV в онлайн-конвертер, получите JSON, затем проверьте результат в форматировщике JSON, чтобы убедиться в корректности структуры.

Преимущество: данные никогда не покидают браузер. Никаких загрузок, никакой серверной обработки, никаких вопросов приватности. Это важно при работе с внутренними данными, API-ключами в экспортах или чем угодно, что вы не хотите отправлять стороннему серверу.

Лучше всего подходит для: небольших файлов (до 10 МБ), быстрых разовых конвертаций, нетехнических членов команды.

Способ 2 — JavaScript / Node.js

Браузер (vanilla JS):

function csvToJson(csv) {
  const lines = csv.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, i) => {
      obj[header] = values[i]?.trim() ?? '';
      return obj;
    }, {});
  });
}

const csv = `name,age,city
Alice,30,New York
Bob,25,London`;

console.log(JSON.stringify(csvToJson(csv), null, 2));

Это работает для простого CSV без полей в кавычках. Для production с запятыми внутри значений, переносами строк в полях или строками в кавычках берите полноценный парсер.

Node.js (csv-parser + потоки):

import { createReadStream } from 'fs';
import { parse } from 'csv-parse';

const records = [];

createReadStream('data.csv')
  .pipe(parse({ columns: true, trim: true, skip_empty_lines: true }))
  .on('data', (row) => records.push(row))
  .on('end', () => {
    console.log(JSON.stringify(records, null, 2));
  });

Опция columns: true берёт первую строку как ключи. Опция trim убирает пробелы из значений. Это корректно обрабатывает поля в кавычках, экранированные запятые и многострочные значения.

Способ 3 — Python

Стандартная библиотека (без зависимостей):

import csv
import json

with open('data.csv', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    rows = list(reader)

with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(rows, f, indent=2, ensure_ascii=False)

csv.DictReader отображает каждую строку в словарь, используя строку заголовка как ключи. Флаг ensure_ascii=False сохраняет Unicode-символы (китайские, японские, с диакритиками) вместо экранирования в \uXXXX.

Pandas (однострочник для data scientist):

import pandas as pd

df = pd.read_csv('data.csv')
df.to_json('data.json', orient='records', indent=2, force_ascii=False)

Когда что выбирать:

  • csv + json: лёгкие скрипты, Lambda-функции, контейнеры, где нужны минимальные зависимости.
  • pandas: когда нужно ещё и почистить, фильтровать или преобразовать данные перед конвертацией. Накладные расходы импорта pandas оправданы, если вы делаете больше, чем просто конвертация формата.

Способ 4 — CLI-инструменты

Для shell-скриптов и конвейеров автоматизации:

csvkit:

# Установка: pip install csvkit
csvjson data.csv > data.json

Miller (mlr):

# Установка: brew install miller (macOS) или apt install miller (Ubuntu)
mlr --csv --json cat data.csv > data.json

Конвейер с jq для фильтрации:

# Конвертация и фильтрация в одном пайплайне
csvjson data.csv | jq '[.[] | select(.age | tonumber > 25)]'

Miller особенно силён, потому что нативно работает с CSV, JSON, TSV и другими форматами. Можно преобразовать данные при конвертации:

# Конвертируем CSV в JSON, переименовываем поле, добавляем вычисляемое поле
mlr --csv --json rename name,fullName then put '$age_group = ($age > 30) ? "senior" : "junior"' data.csv

Подробнее о jq-паттернах для предобработки JSON см. в шпаргалке по jq.

JSON в CSV: обратное направление

Конвертация JSON в CSV приносит сложности, отсутствующие в прямом направлении.

Сплющивание вложенных объектов

CSV по природе плоский. Когда в JSON есть вложенные объекты, нужна стратегия сплющивания:

{
  "name": "Alice",
  "address": {
    "city": "New York",
    "zip": "10001"
  }
}

Превращается в:

nameaddress.cityaddress.zip
AliceNew York10001

Точечная нотация (address.city) — самый распространённый подход. На Python:

import pandas as pd

data = [
    {"name": "Alice", "address": {"city": "New York", "zip": "10001"}},
    {"name": "Bob", "address": {"city": "London", "zip": "EC1A"}}
]

df = pd.json_normalize(data)
df.to_csv('output.csv', index=False)
# Колонки: name, address.city, address.zip

Обработка массивов

Поля-массивы требуют решения:

СтратегияПример входаCSV-выводЛучший сценарий
Объединить в строку["admin","editor"]admin;editorПростые списки, обратимо
Развернуть в колонки["admin","editor"]role_0: admin, role_1: editorМассивы фиксированной длины
Развернуть в строки["admin","editor"]Две строки, по одной на рольРеляционный анализ

Выбирайте по потребителю. Если CSV возвращается в БД, развёртывание в строки обычно оправданнее.

Потеря информации о типах

В CSV нет системы типов. При конвертации JSON в CSV:

  • true становится строкой "true" — это boolean или строка?
  • null становится пустой ячейкой — не отличается от пустой строки ""
  • 42 становится "42" — это число или строка?

Если важна точность round-trip (CSV → обработка → JSON), задокументируйте свои конвенции типов в комментарии заголовка или сопутствующем файле схемы.

Пять распространённых ловушек и как их избежать

Эти проблемы тихо портят данные. Большинство туториалов их пропускает. Не учитесь на проблемах в продакшене.

1. Минное поле кодировок

Проблема: открываете CSV от коллеги и видите é вместо é или 锟斤拷 вместо китайских иероглифов.

Почему: файл сохранён в одной кодировке (Windows-1252, GBK, Shift_JIS), а парсер ожидает UTF-8. Excel под Windows часто сохраняет CSV в Windows-1252 или добавляет UTF-8 BOM (Byte Order Mark — невидимые \xEF\xBB\xBF в начале файла).

Решение:

# Сначала определим кодировку
import chardet

with open('data.csv', 'rb') as f:
    result = chardet.detect(f.read(10000))
    print(result)  # {'encoding': 'Windows-1252', 'confidence': 0.73}

# Затем читаем с правильной кодировкой
with open('data.csv', encoding=result['encoding']) as f:
    reader = csv.DictReader(f)
    # ...

В Node.js явно срезаем BOM:

import { readFileSync } from 'fs';

let content = readFileSync('data.csv', 'utf-8');
// Срезаем UTF-8 BOM, если есть
if (content.charCodeAt(0) === 0xFEFF) {
  content = content.slice(1);
}

2. Путаница с разделителем

Проблема: парсер выдаёт одну гигантскую колонку вместо нескольких полей.

Почему: во многих европейских локалях (Франция, Германия, Испания) Excel применяет точку с запятой (;) как разделитель CSV, потому что запятая — десятичный разделитель (например, 3,14 вместо 3.14). Tab-separated файлы (.tsv) добавляют ещё один вариант.

Решение: автоматически определять разделитель по первым строкам:

import csv

with open('data.csv') as f:
    sample = f.read(8192)
    dialect = csv.Sniffer().sniff(sample, delimiters=',;\t|')
    f.seek(0)
    reader = csv.DictReader(f, dialect=dialect)

3. Ведущие нули исчезают

Проблема: zip-код 00501 становится 501. Код товара 007 становится 7.

Почему: парсер (или Excel) интерпретирует поле как число и срезает ведущие нули. Особенно опасно для zip-кодов, телефонных номеров и кодов идентификаторов.

Решение: принудительно строковый тип. В pandas:

df = pd.read_csv('data.csv', dtype={'zip': str, 'product_code': str})

В JavaScript проверяем, отличается ли исходная строка от её числового представления:

function preserveLeadingZeros(value) {
  if (/^0\d+$/.test(value)) return value; // Оставляем как строку
  const num = Number(value);
  return isNaN(num) ? value : num;
}

4. Потеря точности больших чисел

Проблема: ID 9007199254740993 становится 9007199254740992 в JSON.

Почему: JavaScript Number — это 64-битный float (IEEE 754). Целые выше Number.MAX_SAFE_INTEGER (2^53 − 1 = 9007199254740991) теряют точность. Это бьёт по ID в БД, Snowflake ID и Twitter/X-постам. Сравнение Snowflake с UUID v7 и другими ID-форматами разобрано в сравнении UUID v4, v7, ULID и Snowflake.

Решение: храните большие числа как строки в JSON или применяйте BigInt в коде обработки:

// Парсинг с сохранением строки для больших чисел
function safeParseNumber(value) {
  const num = Number(value);
  if (Number.isInteger(num) && !Number.isSafeInteger(num)) {
    return value; // Оставляем как строку для сохранения точности
  }
  return isNaN(num) ? value : num;
}

5. Неоднозначность пустых значений

Проблема: в CSV пустые ячейки. После конвертации непонятно, было ли исходное значение пустой строкой "", null или просто отсутствовало.

Почему: в CSV нет способа различить эти три состояния. Пустое поле между двумя запятыми (Alice,,30) может означать любое из трёх.

Решение: задайте конвенцию и применяйте её последовательно:

def parse_value(value):
    if value == '':
        return None        # или '' — выбор за вами
    if value == 'NULL' or value == 'null':
        return None
    return value

Если данные используют sentinel-значения вроде NULL, N/A или -, документируйте и обрабатывайте их явно.

Стриминг больших файлов

Когда CSV превышает 100 МБ, загружать его целиком в память — не вариант. Применяйте стриминг.

Node.js (поток через pipeline):

import { createReadStream, createWriteStream } from 'fs';
import { parse } from 'csv-parse';
import { Transform } from 'stream';
import { pipeline } from 'stream/promises';

let first = true;
const toJsonArray = new Transform({
  objectMode: true,
  transform(record, encoding, callback) {
    const prefix = first ? '[\n' : ',\n';
    first = false;
    callback(null, prefix + JSON.stringify(record));
  },
  flush(callback) {
    callback(null, '\n]');
  }
});

await pipeline(
  createReadStream('large.csv'),
  parse({ columns: true, trim: true }),
  toJsonArray,
  createWriteStream('large.json')
);

Python (генератор):

import csv
import json

def csv_rows(path):
    with open(path, encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            yield row

# Стримим в JSON Lines (по одному JSON-объекту на строку)
with open('large.jsonl', 'w', encoding='utf-8') as out:
    for row in csv_rows('large.csv'):
        out.write(json.dumps(row, ensure_ascii=False) + '\n')

Для очень больших файлов рассмотрите формат JSON Lines (.jsonl) вместо одного JSON-массива. Каждая строка — независимый JSON-объект, и выходной файл тоже можно обрабатывать построчно — без разбора всего разом.

Часто задаваемые вопросы

В чём разница между CSV и JSON?

CSV (Comma-Separated Values) хранит данные как плоскую таблицу, где всякое значение — строка. JSON (JavaScript Object Notation) хранит структурированные данные с вложенными объектами, массивами и типизированными значениями (строки, числа, boolean, null). CSV меньше и удобен в таблицах; JSON выразительнее и удобен для API.

Как конвертировать CSV в JSON в JavaScript?

Применяйте пакет csv-parse в Node.js: читайте файл createReadStream, прокидывайте через parse({ columns: true }) и собирайте результаты. Для браузера читайте файл через FileReader, разбейте по переводам строк и отображайте строки в объекты, используя строку заголовка как ключи.

Как конвертировать CSV в JSON в Python?

Применяйте csv.DictReader из стандартной библиотеки, чтобы читать строки как словари, затем json.dump(), чтобы записать их как JSON-массив. Для манипуляций с данными перед конвертацией однострочник pandas.read_csv() плюс df.to_json(orient='records') — отличная альтернатива.

Можно ли конвертировать вложенный JSON в CSV?

Да, но нужна стратегия сплющивания. Самый распространённый подход — точечная нотация: поле вроде address.city становится заголовком колонки. На Python pandas.json_normalize() справляется автоматически. Массивы требуют дополнительных решений — объединить в строки, развернуть в колонки или развернуть в строки.

Почему мой CSV содержит «крякозябры» после конвертации?

Несовпадение кодировок. Файл, скорее всего, сохранён в Windows-1252 или GBK, а парсер ожидает UTF-8. Применяйте библиотеку определения вроде chardet (Python), затем явно укажите кодировку при чтении. Также проверьте на UTF-8 BOM, который некоторые инструменты добавляют автоматически.

Как обработать CSV-файл больше 100 МБ?

Применяйте стриминг вместо загрузки в память. В Node.js — потоки через csv-parse. В Python — итерация через csv.DictReader и генератор. Рассмотрите формат JSON Lines (.jsonl) вместо одного JSON-массива для удобства последующей обработки.

Как проверить, что сконвертированный JSON валиден?

Вставьте вывод в онлайн-форматировщик JSON, чтобы проверить синтаксис, структуру и вложенность. Для автоматической валидации применяйте JSON.parse() в JavaScript или json.loads() в Python — оба бросают понятные ошибки на невалидном вводе. Для валидации схемы определите JSON Schema и валидируйте программно.

Главные выводы

  1. Выбирайте подходящий способ под контекст: браузерные инструменты для разовых задач, код для автоматизации, CLI для конвейеров.
  2. Следите за проблемами кодировок — всегда явно указывайте кодировку, не полагайтесь на дефолты.
  3. Сохраняйте типы намеренно — ведущие нули, большие целые и null-значения требуют явной обработки.
  4. Стримьте большие файлы вместо загрузки в память. Рассмотрите JSON Lines для очень больших датасетов.
  5. Валидируйте вывод — прогоните сконвертированный JSON через форматировщик JSON, чтобы поймать структурные проблемы до продакшена.

Похожие статьи

Все статьи