CSV to JSON JavaScript — Converter + Code Examples

·Front-end & Node.js Developer·ПровереноSophie Laurent·Опубликовано

Используйте бесплатный CSV to JSON прямо в браузере — установка не требуется.

Попробовать CSV to JSON онлайн →

Большинство CSV-данных, с которыми приходится работать, поступает в виде плоской строки из загруженного файла, экспорта базы данных или API, который до сих пор использует формат из 70-х годов. Чтобы конвертировать CSV в JSON в JavaScript, вам нужны две вещи, которые язык предоставляет бесплатно: разбиение строк для парсинга строк и JSON.stringify() для сериализации результата. Для базовых задач npm-пакеты не нужны — это руководство охватывает полный пайплайн от переиспользуемой утилиты csvToJson() до PapaParse и файлового ввода-вывода в Node.js. Для быстрых конвертаций без написания кода онлайн-конвертер CSV в JSON справляется мгновенно. Все примеры рассчитаны на Node.js 18+ и современные браузеры.

  • Разбейте CSV по переносу строки, извлеките заголовки из строки 0, преобразуйте оставшиеся строки в объекты, затем JSON.stringify(array, null, 2) для форматированного вывода.
  • JSON.stringify() возвращает строку; JSON.parse() преобразует её обратно в живой JavaScript-массив — важно знать, с чем вы работаете, прежде чем выполнять операции.
  • Экземпляры Map не сериализуются в JSON автоматически — сначала вызовите Object.fromEntries(map).
  • Для CSV с экранированными полями, запятыми внутри значений или переносами строк в ячейках используйте PapaParse или csv-parse вместо ручного разбиения.
  • csvtojson (npm) обрабатывает приведение типов, стриминг и граничные случаи RFC 4180 в одном вызове.

Что такое конвертация CSV в JSON?

Конвертация CSV в JSON преобразует плоский текстовый формат с разделителями-запятыми в структурированный массив объектов, где каждая строка становится JavaScript-объектом с ключами из заголовков столбцов. Формат CSV не имеет типов данных — всё является строкой. JSON добавляет структуру, вложенность и явные типы (числа, булевы, null). Эта конвертация является первым шагом практически в каждом пайплайне обработки данных, который начинается с экспорта таблицы, выгрузки из устаревшей системы или загруженного пользователем файла. Сами данные остаются теми же; формат меняется от столбцов на основе позиций к именованным свойствам.

Before · json
After · json
name,email,role,active
Алексей Иванов,ivanov@nexuslabs.io,Engineering Lead,true
Мария Соколова,sokolova@nexuslabs.io,Product Manager,true
[
  {
    "name": "Алексей Иванов",
    "email": "ivanov@nexuslabs.io",
    "role": "Engineering Lead",
    "active": "true"
  },
  {
    "name": "Мария Соколова",
    "email": "sokolova@nexuslabs.io",
    "role": "Product Manager",
    "active": "true"
  }
]

csvToJson() — создание переиспользуемой функции конвертации

Полный пайплайн CSV-в-JSON в JavaScript состоит из трёх шагов: разбить CSV-строку по переносу строки для получения строк, извлечь заголовки из первой строки с помощью split(','), затем преобразовать каждую оставшуюся строку в обычный JavaScript-объект, где ключи берутся из заголовков, а значения — из соответствующих позиций столбцов. Финальный вызов JSON.stringify() преобразует этот массив объектов в JSON-строку. Вот минимальная рабочая версия:

JavaScript — минимальная csvToJson
function csvToJson(csv) {
  const lines = csv.trim().split('\n')
  const headers = lines[0].split(',').map(h => h.trim())

  const rows = lines.slice(1)
    .filter(line => line.trim() !== '')
    .map(line => {
      const values = line.split(',')
      return Object.fromEntries(
        headers.map((header, i) => [header, values[i]?.trim()])
      )
    })

  return JSON.stringify(rows, null, 2)
}

const csv = `server,port,region,status
api-gateway,8080,us-east-1,healthy
auth-service,8443,eu-west-1,degraded
payments-api,9090,ap-south-1,healthy`

console.log(csvToJson(csv))
// [
//   { "server": "api-gateway", "port": "8080", "region": "us-east-1", "status": "healthy" },
//   { "server": "auth-service", "port": "8443", "region": "eu-west-1", "status": "degraded" },
//   { "server": "payments-api", "port": "9090", "region": "ap-south-1", "status": "healthy" }
// ]

Эта функция обрабатывает базовые случаи: завершающие переносы строк, пустые строки, пробелы вокруг значений. Каждое поле CSV приходит как строка. Обратите внимание, что port является "8080" (строкой), а не 8080 (числом). Если вам нужны правильные типы в JSON-выводе, необходимо приводить их самостоятельно. Вот расширенная версия с определением типов:

JavaScript — csvToJson с приведением типов
function coerceValue(val) {
  if (val === undefined || val === '') return null
  if (val === 'true') return true
  if (val === 'false') return false
  if (val === 'null') return null
  const num = Number(val)
  if (!isNaN(num) && val.trim() !== '') return num
  return val
}

function csvToJson(csv, { coerce = false } = {}) {
  const lines = csv.trim().split('\n')
  const headers = lines[0].split(',').map(h => h.trim())

  const rows = lines.slice(1)
    .filter(line => line.trim() !== '')
    .map(line => {
      const values = line.split(',')
      return Object.fromEntries(
        headers.map((header, i) => [
          header,
          coerce ? coerceValue(values[i]?.trim()) : values[i]?.trim()
        ])
      )
    })

  return JSON.stringify(rows, null, 2)
}

const csv = `endpoint,port,max_connections,debug
/api/v2/orders,8443,500,true
/api/v2/health,8080,100,false`

console.log(csvToJson(csv, { coerce: true }))
// [
//   { "endpoint": "/api/v2/orders", "port": 8443, "max_connections": 500, "debug": true },
//   { "endpoint": "/api/v2/health", "port": 8080, "max_connections": 100, "debug": false }
// ]

Флаг coerce является опциональным, поскольку автоматическое определение типов может дать сбой — поле вроде почтового индекса ("07302") теряет ведущий ноль при преобразовании в число. Держите приведение отключённым по умолчанию и включайте только тогда, когда вы контролируете схему. Небольшое замечание: JSON.stringify() принимает третий аргумент space для отступа. Передайте 2 для двух пробелов, 4 для четырёх, или "\t" для табуляции. Опустите полностью для компактного однострочного вывода — это удобно при отправке JSON-строки как тела API-запроса, где пробелы лишь занимают полосу пропускания.

Примечание:Сырая CSV-строка — это не JSON. Вызов JSON.parse() напрямую на CSV-тексте выбрасывает SyntaxError. Сначала необходимо преобразовать CSV в JavaScript-объекты с помощью вашей функции csvToJson(), которая внутри вызывает JSON.stringify() для создания настоящей JSON-строки.

Работа с Map, датами и пользовательскими объектами из CSV-данных

Не каждая конвертация CSV заканчивается плоским массивом обычных объектов. Иногда нужно построить Map из пар заголовок-значение, разобрать строки дат в объекты Date или добавить вычисляемые свойства перед сериализацией. У JavaScript есть особенность, которая часто сбивает с толку: экземпляры Map не сериализуются с помощью JSON.stringify(). Вы получаете пустой объект. Решение — Object.fromEntries() для преобразования Map обратно в обычный объект перед сериализацией.

Причина, по которой Map сериализуется в {}, заключается в том, что JSON.stringify() перебирает собственные перечисляемые свойства объекта. Map хранит свои записи во внутреннем слоте, а не как перечисляемые свойства самого объекта, поэтому сериализатор видит объект без ключей. У прототипа Map также нет метода toJSON(), который JSON.stringify() вызывает первым для любого значения перед принятием решения о способе сериализации. Если у значения есть toJSON(), возвращаемое этим методом значение и будет сериализовано — а не сам объект. Именно поэтому объекты Date сериализуются корректно: Date.prototype.toJSON возвращает строку ISO 8601, поэтому JSON.stringify(new Date()) создаёт строку-временную метку, а не пустой объект. Понимание этого механизма позволяет определить то же поведение в собственных классах — как показано в примере с EmployeeRecord ниже — чтобы контролировать, какие поля из CSV попадут в итоговый JSON-вывод.

Преобразование Map в JSON

JavaScript — Map в JSON через Object.fromEntries()
// Строим Map из пар заголовок→значение CSV
const headers = ['server', 'port', 'region']
const values = ['payments-api', '9090', 'ap-south-1']
const rowMap = new Map(headers.map((h, i) => [h, values[i]]))

// Map НЕ сериализуется напрямую
console.log(JSON.stringify(rowMap))
// "{}"  — пустой объект, данные потеряны!

// Сначала преобразуем в обычный объект
const rowObj = Object.fromEntries(rowMap)
console.log(JSON.stringify(rowObj, null, 2))
// {
//   "server": "payments-api",
//   "port": "9090",
//   "region": "ap-south-1"
// }

Строки дат и toJSON()

Поля дат из CSV приходят как строки. Если вы разбираете их в объекты Date при обработке, эти даты сериализуются корректно, поскольку у Date есть встроенный метод toJSON(), возвращающий строку ISO 8601. Вы также можете определить пользовательский toJSON() в собственных классах для управления тем, какие столбцы CSV появятся в сериализованном выводе — например, чтобы исключить внутренние поля отслеживания, такие как _rowIndex.

JavaScript — toJSON() для пользовательской сериализации
class EmployeeRecord {
  constructor(csvRow) {
    this._rowIndex = csvRow._rowIndex  // внутреннее, не для JSON
    this.employeeId = csvRow.employee_id
    this.name = csvRow.name
    this.hiredAt = new Date(csvRow.hired_date)
    this.salary = Number(csvRow.salary)
  }

  toJSON() {
    // Экспортируем только нужные поля
    return {
      employee_id: this.employeeId,
      name: this.name,
      hired_at: this.hiredAt,  // Date.toJSON() → ISO-строка автоматически
      salary: this.salary,
    }
  }
}

const csvRow = {
  _rowIndex: 42,
  employee_id: 'EMP-2847',
  name: 'Алексей Иванов',
  hired_date: '2024-03-15',
  salary: '128000',
}

const record = new EmployeeRecord(csvRow)
console.log(JSON.stringify(record, null, 2))
// {
//   "employee_id": "EMP-2847",
//   "name": "Алексей Иванов",
//   "hired_at": "2024-03-15T00:00:00.000Z",
//   "salary": 128000
// }
// Примечание: _rowIndex исключён, salary — число, дата в формате ISO

Reviver для десериализации дат

После записи JSON из CSV в файл или отправки по сети JSON.parse() вернёт обычные объекты — объекты Date снова станут строками. Используйте функцию reviver для преобразования строк ISO 8601 обратно в объекты Date при разборе:

JavaScript — reviver для восстановления объектов Date
const jsonString = '{"employee_id":"EMP-2847","name":"Алексей Иванов","hired_at":"2024-03-15T00:00:00.000Z","salary":128000}'

const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/

const parsed = JSON.parse(jsonString, (key, value) => {
  if (typeof value === 'string' && isoDatePattern.test(value)) {
    return new Date(value)
  }
  return value
})

console.log(parsed.hired_at instanceof Date)  // true
console.log(parsed.hired_at.getFullYear())    // 2024
Внимание:JSON.stringify() возвращает undefined (а не строку) для значений, содержащих функции, символы (Symbol) или свойства со значением undefined. Если ваши объекты из CSV случайно получают значение undefined из отсутствующего столбца, это свойство молча исчезнет из JSON-вывода. Всегда используйте null вместо отсутствующих значений.

Справочник параметров JSON.stringify()

Сигнатура функции: JSON.stringify(value, replacer?, space?). Аргументы replacer и space оба необязательны — передайте null для replacer, когда вам нужен только отступ.

Параметр
Тип
По умолчанию
Описание
value
any
(обязательный)
Значение JavaScript для сериализации — объект, массив, строка, число, булево или null
replacer
Function | Array | null
undefined
Фильтрует или преобразует значения при сериализации. Массив задаёт белый список имён свойств; функция получает пары (key, value)
space
number | string
undefined
Отступ: число 0–10 для пробелов, строка (например "\t") для произвольного отступа. Без этого параметра — компактный однострочный вывод

Параметры JSON.parse():

Параметр
Тип
По умолчанию
Описание
text
string
(обязательный)
JSON-строка для разбора — должна быть валидным JSON, иначе выбрасывается SyntaxError
reviver
Function | undefined
undefined
Вызывается для каждой пары ключ-значение. Возвращаемое значение заменяет исходное; return undefined удаляет свойство

JSON.parse() — потребление JSON-вывода

Получив JSON-строку из csvToJson(), следующим шагом обычно является разбор её обратно в живой JavaScript-массив для фильтрации, преобразования или передачи в API. Важно различать JSON-строку (typeof === "string") и JavaScript-объект. Вы не можете вызвать .filter() или обратиться к [0].name на строке — сначала нужен JSON.parse(). Этот цикл (stringify затем parse) также работает как техника валидации: если конвертация CSV создала что-то, что не является валидным JSON, parse выбросит исключение. Необязательный аргумент reviver позволяет трансформировать каждую пару ключ-значение при разборе — полезно для восстановления объектов Date из ISO-строк или переименования ключей без отдельного прохода.

JavaScript — разбор JSON-вывода и запрос строк
const csv = `endpoint,method,avg_latency_ms,error_rate
/api/v2/orders,POST,342,0.02
/api/v2/health,GET,12,0.00
/api/v2/payments,POST,890,0.15
/api/v2/users,GET,45,0.01`

// Шаг 1: конвертируем CSV в JSON-строку
const jsonString = csvToJson(csv, { coerce: true })

// Шаг 2: разбираем обратно в JavaScript-массив
const endpoints = JSON.parse(jsonString)

// Проверяем, что это массив
console.log(Array.isArray(endpoints))  // true

// Фильтруем медленные эндпоинты
const slow = endpoints.filter(ep => ep.avg_latency_ms > 200)
console.log(slow.map(ep => ep.endpoint))
// ["/api/v2/orders", "/api/v2/payments"]

// Деструктурируем первую строку
const [first, ...rest] = endpoints
console.log(first.endpoint)  // "/api/v2/orders"
console.log(rest.length)     // 3

Безопасная обёртка для JSON.parse() полезна при валидации результата конвертации перед дальнейшей обработкой. Если конвертация CSV по какой-то причине создала некорректный JSON (усечённый ввод, ошибки кодировки), это перехватит ошибку без сбоя программы:

JavaScript — безопасная обёртка parse
function safeParse(jsonString) {
  try {
    return { data: JSON.parse(jsonString), error: null }
  } catch (err) {
    return { data: null, error: err.message }
  }
}

// Валидный вывод
const result = safeParse(csvToJson(csv))
if (result.error) {
  console.error('CSV-конвертация создала невалидный JSON:', result.error)
} else {
  console.log(`Разобрано ${result.data.length} строк`)
}

// Случайная передача сырого CSV в JSON.parse — это упадёт
const bad = safeParse('name,email\nАлексей,ivanov@nexuslabs.io')
console.log(bad.error)  // "Unexpected token 'a', "name,email"... is not valid JSON"

Reviver для переименования ключей и валидации

Функция reviver получает каждую пару ключ-значение при разборе, от внутренних свойств наружу. Возврат undefined для ключа полностью удаляет его из результата; возврат другого значения заменяет его. Reviver полезен для переименования заголовков (camelCase в snake_case), удаления внутренних полей или проверки наличия обязательных столбцов. Он вызывается с корневым значением последним (с ключом в виде пустой строки), где следует выбрасывать исключение, если результат не является массивом.

JavaScript — reviver для переименования ключей и валидации формы
const jsonString = csvToJson(`employeeId,firstName,hiredDate
EMP-2847,Алексей,2024-03-15
EMP-3012,Мария,2023-11-01`, { coerce: false })

const camelToSnake = str => str.replace(/[A-Z]/g, c => '_' + c.toLowerCase())

const employees = JSON.parse(jsonString, function(key, value) {
  // Корневое значение — валидируем форму
  if (key === '') {
    if (!Array.isArray(value)) throw new Error('Expected JSON array from CSV')
    return value
  }
  // Переименовываем ключи camelCase в snake_case
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
    return Object.fromEntries(
      Object.entries(value).map(([k, v]) => [camelToSnake(k), v])
    )
  }
  return value
})

console.log(employees[0])
// { employee_id: 'EMP-2847', first_name: 'Алексей', hired_date: '2024-03-15' }

Конвертация CSV из файла и ответа API

Два реальных источника CSV-данных в продакшне: файлы на диске и HTTP-ответы. Оба сценария требуют обработки ошибок, поскольку ввод является внешним и неконтролируемым.

Читаем CSV-файл, конвертируем, записываем JSON

Node.js 18+ — конвертация файла
import { readFileSync, writeFileSync } from 'node:fs'

function csvToJsonFromFile(inputPath, outputPath) {
  let csvText
  try {
    csvText = readFileSync(inputPath, 'utf8')
  } catch (err) {
    throw new Error(`Failed to read ${inputPath}: ${err.message}`)
  }

  const lines = csvText.trim().split('\n')
  if (lines.length < 2) {
    throw new Error(`${inputPath} has no data rows (only ${lines.length} line)`)
  }

  const headers = lines[0].split(',').map(h => h.trim())
  const rows = lines.slice(1)
    .filter(line => line.trim() !== '')
    .map(line => {
      const values = line.split(',')
      return Object.fromEntries(headers.map((h, i) => [h, values[i]?.trim()]))
    })

  const jsonOutput = JSON.stringify(rows, null, 2)
  writeFileSync(outputPath, jsonOutput, 'utf8')
  console.log(`Конвертировано ${rows.length} строк → ${outputPath}`)
  return rows
}

// Использование
const data = csvToJsonFromFile('inventory.csv', 'inventory.json')
console.log(data[0])
// { sku: "WDG-2847", warehouse: "us-east-1", quantity: "150", ... }

Получение CSV из API-эндпоинта

Node.js 18+ — конвертация ответа API
async function fetchCsvAsJson(url) {
  const response = await fetch(url)
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`)
  }

  const contentType = response.headers.get('content-type') || ''
  if (!contentType.includes('text/csv') && !contentType.includes('text/plain')) {
    console.warn(`Unexpected content-type: ${contentType}`)
  }

  const csvText = await response.text()
  const lines = csvText.trim().split('\n')
  const headers = lines[0].split(',').map(h => h.trim())
  const rows = lines.slice(1)
    .filter(line => line.trim() !== '')
    .map(line => {
      const values = line.split(',')
      return Object.fromEntries(headers.map((h, i) => [h, values[i]?.trim()]))
    })

  return rows
}

// Пример: получаем CSV с курсами валют от поставщика данных
try {
  const rates = await fetchCsvAsJson('https://data.ecb.internal/rates/daily.csv')
  console.log(JSON.stringify(rates.slice(0, 3), null, 2))
  // Отправляем как JSON в нижестоящий сервис
  await fetch('https://api.internal/v2/rates', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ data: rates }),
  })
} catch (err) {
  console.error('Rate sync failed:', err.message)
}
Примечание:Аргумент replacer в JSON.stringify() позволяет задать белый список конкретных столбцов из CSV. Передайте массив имён заголовков, чтобы включить только эти поля: JSON.stringify(rows, ['name', 'email', 'department']). Свойства, не указанные в массиве, молча исключаются из вывода.

Конвертация CSV в JSON из командной строки

Node.js умеет запускать встроенные скрипты, а для конвертации CSV в JSON без написания скрипта существуют специализированные CLI-инструменты.

bash — однострочник Node.js
# Передаём CSV во встроенный скрипт Node.js
cat servers.csv | node -e "
  const lines = require('fs').readFileSync('/dev/stdin','utf8').trim().split('\n');
  const h = lines[0].split(',');
  const rows = lines.slice(1).map(l => Object.fromEntries(h.map((k,i) => [k.trim(), l.split(',')[i]?.trim()])));
  console.log(JSON.stringify(rows, null, 2));
"
bash — Miller (mlr) для CSV в JSON
# Miller — швейцарский нож для структурированных данных
# Установка: brew install miller (macOS) или apt install miller (Debian/Ubuntu)
mlr --icsv --ojson cat inventory.csv

# Фильтрация строк при конвертации
mlr --icsv --ojson filter '$quantity > 100' inventory.csv

# Выбор конкретных столбцов
mlr --icsv --ojson cut -f sku,warehouse,quantity inventory.csv
bash — csvtojson CLI
# Глобальная установка
npm install -g csvtojson

# Конвертация файла
csvtojson servers.csv > servers.json

# Передача из stdin
cat exports/q1-metrics.csv | csvtojson > q1-metrics.json

Для больших файлов Miller обычно предпочтительнее csvtojson. Miller написан на C и обрабатывает CSV как поток без загрузки всего файла в память, что позволяет обрабатывать многогигабайтные экспорты при постоянном потреблении памяти. Он также поддерживает операции на уровне полей — переименование столбцов, приведение типов, фильтрацию строк — до того, как данные станут JSON, что позволяет избежать двухэтапного пайплайна разбор-затем-преобразование. csvtojson, с другой стороны, работает в Node.js и удобнее, когда весь ваш инструментарий — это JavaScript: вы можете передавать его вывод напрямую в Node-стримы, импортировать как библиотеку или использовать его colParser API для приведения типов по столбцам в коде. Предпочитайте Miller для максимальной пропускной способности и конвейеров оболочки; предпочитайте csvtojson, когда нужна тесная интеграция с Node.js-приложением.

Примечание:jq не умеет разбирать CSV нативно. Если вам нужен jq в пайплайне, сначала конвертируйте в JSON с помощью csvtojson или mlr, затем передайте JSON-вывод в jq для фильтрации и преобразования.

Высокопроизводительная альтернатива — PapaParse

Ручной подход с split(',') не работает на реальных CSV-файлах. Экранированные поля с запятыми, встроенные переносы строк, экранированные двойные кавычки — всё это ломает наивный разделитель. PapaParse — это библиотека, к которой я обращаюсь, когда CSV поступает из неизвестного источника. Она обрабатывает все граничные случаи RFC 4180, автоматически определяет разделители и работает как в Node.js, так и в браузерах.

bash — установка PapaParse
npm install papaparse
JavaScript — PapaParse с приведением типов
import Papa from 'papaparse'

const csv = `product,description,price,in_stock
"Widget, Large","A premium widget with ""extra"" features",29.99,true
Bolt Assembly,Standard M8 bolt kit,4.50,true
"Gasket Set","Includes gasket, seal, and O-ring",12.75,false`

const { data, errors, meta } = Papa.parse(csv, {
  header: true,
  dynamicTyping: true,     // автоматически конвертирует числа и булевы
  skipEmptyLines: true,
  transformHeader: h => h.trim().toLowerCase().replace(/\s+/g, '_'),
})

if (errors.length > 0) {
  console.error('Parse errors:', errors)
}

console.log(JSON.stringify(data, null, 2))
// [
//   {
//     "product": "Widget, Large",
//     "description": "A premium widget with \"extra\" features",
//     "price": 29.99,
//     "in_stock": true
//   },
//   ...
// ]
console.log(`Разобрано ${data.length} строк, разделитель: "${meta.delimiter}"`)

Опция dynamicTyping в PapaParse выполняет приведение типов автоматически — числа становятся числами, "true"/"false" — булевыми значениями. Коллбэк transformHeader нормализует имена столбцов в snake_case, избавляя вас от работы с непоследовательными заголовками из разных CSV-экспортов. Для быстрых конвертаций без написания кода конвертер CSV в JSON обрабатывает всё это в браузере.

Вывод в терминале с подсветкой синтаксиса

Большой JSON-массив в терминале очень трудно читать. Подсветка синтаксиса делает вывод читаемым при отладке и разработке. Пакет cli-highlight раскрашивает JSON-вывод в Node.js-терминалах.

bash — установка cli-highlight
npm install cli-highlight
JavaScript — цветной JSON-вывод в терминале
import { highlight } from 'cli-highlight'

// После конвертации CSV в JSON-массив
const jsonOutput = JSON.stringify(rows, null, 2)

// Печатаем с подсветкой синтаксиса
console.log(highlight(jsonOutput, { language: 'json' }))
// Ключи, строки, числа и булевы значения получают разные цвета

Цветной вывод особенно полезен при интерактивной проверке большого результата конвертации. JSON-ключи, строковые значения, числа и булевы значения получают разные ANSI-цвета, что упрощает обнаружение поля с неправильным типом — например, номер порта, который должен быть 8080, но подсвечивается как строка, потому что приведение типов было отключено. Это особенно полезно при отладке CSV-файлов, экспортированных из табличных инструментов, где типы столбцов непоследовательны по строкам. Без цвета просмотр 50 строк JSON в поисках одного поля с неправильным типом требует чтения каждого значения по отдельности. С цветом строка-число сразу бросается в глаза.

Внимание:Подсветка синтаксиса добавляет ANSI escape-коды в вывод. Не используйте её при записи JSON в файл, передаче другой программе или отправке как тело API-ответа — escape-коды испортят JSON. Используйте подсветку только для отображения в терминале.

Работа с большими CSV-файлами

Загрузка CSV-файла размером 500 МБ в строку с помощью readFileSync() займёт много памяти и может привести к сбою процесса. Для больших файлов обрабатывайте CSV построчно и выдавайте JSON-объекты по мере поступления. Пакет csv-parse (часть экосистемы csv на npm) предоставляет потоковый парсер, работающий с Node.js-стримами.

Стриминг CSV в NDJSON с csv-parse

NDJSON (Newline-Delimited JSON) — формат, где каждая строка выходного файла является самостоятельным JSON-объектом. В отличие от единого большого JSON-массива — который требует нахождения всего файла в памяти, прежде чем начать его читать — файлы NDJSON можно обрабатывать построчно. Это делает NDJSON идеальным для больших наборов данных, которые будут потребляться обработчиками логов, конвейерами стримов или базами данных с API массовой загрузки. Пакет csv-parse выдаёт один JavaScript-объект на строку CSV в объектном режиме, поэтому его можно напрямую передавать в transform-стрим, который добавляет \n после каждого JSON.stringify(row).

Node.js 18+ — стриминг CSV в NDJSON
import { createReadStream, createWriteStream } from 'node:fs'
import { parse } from 'csv-parse'
import { Transform } from 'node:stream'
import { pipeline } from 'node:stream/promises'

// Преобразуем каждый объект-строку CSV в JSON-строку
const toNdjson = new Transform({
  objectMode: true,
  transform(record, encoding, callback) {
    callback(null, JSON.stringify(record) + '\n')
  },
})

await pipeline(
  createReadStream('telemetry-2026-03.csv'),
  parse({
    columns: true,       // используем первую строку как заголовки
    skip_empty_lines: true,
    trim: true,
    cast: true,          // автоматически конвертируем числа и булевы
  }),
  toNdjson,
  createWriteStream('telemetry-2026-03.ndjson')
)

console.log('Потоковая конвертация завершена')
// Каждая строка выходного файла — один JSON-объект:
// {"timestamp":"2026-03-15T08:22:00Z","service":"gateway","latency_ms":42,"status":200}
// {"timestamp":"2026-03-15T08:22:01Z","service":"auth","latency_ms":156,"status":401}
// ...

Потоковый режим PapaParse для браузера и Node.js

Потоковый режим PapaParse использует коллбэк step, который вызывается один раз на строку вместо сбора всех строк в память. Вы передаёте ему Node.js ReadStream (в Node.js) или объект File (в браузере), а PapaParse обрабатывает разбиение на части внутри. Никакого стрим-пайплайна настраивать не нужно — только коллбэк. Используйте его, когда нужна совместимость с RFC 4180 без подключения csv-parse.

Node.js — потоковый PapaParse для большого файла
import Papa from 'papaparse'
import { createReadStream } from 'node:fs'

let rowCount = 0
const errors = []

const fileStream = createReadStream('warehouse-inventory.csv')

Papa.parse(fileStream, {
  header: true,
  dynamicTyping: true,
  step(result) {
    // Обрабатываем по одной строке — постоянное потребление памяти
    rowCount++
    if (result.data.quantity === 0) {
      errors.push(`Строка ${rowCount}: ${result.data.sku} закончилась`)
    }
  },
  complete() {
    console.log(`Обработано ${rowCount} строк`)
    if (errors.length > 0) {
      console.log(`Обнаружено проблем: ${errors.length}`)
      errors.forEach(e => console.log(`  ${e}`))
    }
  },
  error(err) {
    console.error('Parse failed:', err.message)
  },
})
Примечание:Переходите на стриминг, когда CSV-файл превышает 50 МБ или при обработке неограниченного ввода (WebSocket-фид, server-sent events, stdin по конвейеру). Формат NDJSON (один JSON-объект на строку) зачастую является лучшим форматом вывода, чем огромный JSON-массив для больших наборов данных — его можно обрабатывать построчно, не загружая весь файл в память.

Распространённые ошибки

Вызов JSON.parse() напрямую на CSV-строке

Проблема: CSV — это не JSON. Передача сырой CSV-строки в JSON.parse() выбрасывает SyntaxError, поскольку запятые и переносы строк не являются допустимым синтаксисом JSON.

Решение: Сначала разберите CSV в JavaScript-объекты с помощью split() или библиотеки, затем используйте JSON.stringify() для создания JSON. Вызывайте JSON.parse() только на строках, которые уже являются валидным JSON.

Before · JavaScript
After · JavaScript
const csv = 'name,email\nАлексей Иванов,ivanov@nexuslabs.io'
const data = JSON.parse(csv)
// SyntaxError: Unexpected token 'a'
const csv = 'name,email\nАлексей Иванов,ivanov@nexuslabs.io'
const lines = csv.trim().split('\n')
const headers = lines[0].split(',')
const rows = lines.slice(1).map(line =>
  Object.fromEntries(headers.map((h, i) => [h, line.split(',')[i]]))
)
const json = JSON.stringify(rows, null, 2)  // валидная JSON-строка
Использование toString() вместо JSON.stringify()

Проблема: Вызов toString() на JavaScript-объекте возвращает бесполезную строку '[object Object]' вместо реальных данных. Это молча уничтожает результат конвертации CSV в JSON.

Решение: Всегда используйте JSON.stringify() для преобразования JavaScript-объектов в JSON-строки. toString() предназначен для приведения примитивов к строкам, а не для сериализации.

Before · JavaScript
After · JavaScript
const row = { server: 'api-gateway', port: 8080 }
const output = row.toString()
// "[object Object]"  — данные потеряны
const row = { server: 'api-gateway', port: 8080 }
const output = JSON.stringify(row, null, 2)
// '{"server":"api-gateway","port":8080}'
Разбиение по запятой без обработки экранированных полей

Проблема: Наивный split(",") ломается, когда значения CSV содержат запятые внутри экранированных полей: "Widget, Large" превращается в два отдельных значения вместо одного.

Решение: Используйте PapaParse или csv-parse для любых CSV-данных, которые вы не полностью контролируете. Если необходимо разбирать вручную, реализуйте конечный автомат-парсер, отслеживающий, находится ли текущая позиция внутри экранированного поля.

Before · JavaScript
After · JavaScript
const line = '"Widget, Large","Premium quality",29.99'
const values = line.split(',')
// ["\"Widget", " Large\"", "\"Premium quality\"", "29.99"]
// 4 значения вместо 3 — первое поле разбито неверно
import Papa from 'papaparse'
const { data } = Papa.parse('"Widget, Large","Premium quality",29.99')
// data[0] = ["Widget, Large", "Premium quality", "29.99"]
// 3 значения, корректно разобрано
Забытое приведение типов CSV-значений

Проблема: Без приведения типов port: "8080" остаётся строкой в JSON вместо числа. Нижестоящие системы, ожидающие числовые типы, отклоняют или неправильно обрабатывают данные.

Решение: Применяйте явное приведение типов на этапе преобразования или используйте PapaParse с dynamicTyping: true. Всегда осознанно подходите к тому, какие поля должны быть числовыми.

Before · JavaScript
After · JavaScript
const row = { port: '8443', debug: 'true', workers: '4' }
JSON.stringify(row)
// {"port":"8443","debug":"true","workers":"4"}  — всё строки
const row = {
  port: Number('8443'),           // 8443
  debug: 'true' === 'true',      // true
  workers: Number('4'),           // 4
}
JSON.stringify(row)
// {"port":8443,"debug":true,"workers":4}  — правильные типы

Ручной разбор vs библиотеки — быстрое сравнение

Метод
Форматированный вывод
Валидный JSON
Пользовательские типы
Стриминг
Требует установки
JSON.stringify()
✓ (с параметром space)
✓ через toJSON()/replacer
Нет (встроенный)
JSON.parse()
N/A (разбор)
✓ (валидирует)
✓ через reviver
Нет (встроенный)
csv-parse (Node.js)
✗ (возвращает объекты)
✗ (не JSON)
npm install
PapaParse
✗ (возвращает объекты)
✗ (не JSON)
npm install
csvtojson
✓ через colParser
npm install
jq (CLI)
N/A
Системная установка
Miller (CLI)
N/A
Системная установка

Для быстрых скриптов, где вы контролируете формат CSV и знаете, что нет экранированных полей, встроенный подход с split() + JSON.stringify() работает и не требует никаких зависимостей. Для производственных систем, обрабатывающих загруженные пользователями CSV-файлы, используйте PapaParse в браузере или csv-parse в Node.js — оба корректно обрабатывают RFC 4180 и поддерживают стриминг. Пакет csvtojson — единственный, который напрямую выдаёт JSON, обрабатывая и разбор, и сериализацию в одном вызове. Когда нужен максимально быстрый путь от вставки до результата, конвертер CSV в JSON справляется со всем этим в браузере без какой-либо настройки.

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

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

Разбейте CSV-строку по символу переноса строки, чтобы получить строки, извлеките заголовки из первой строки с помощью split(","), затем преобразуйте оставшиеся строки в объекты с ключами из заголовков. В конце вызовите JSON.stringify(array, null, 2) для форматированного JSON. Этот подход хорошо работает для простых CSV-файлов, в которых вы контролируете формат и знаете, что нет экранированных полей или встроенных запятых. Для данных из экспорта таблиц или сторонних систем экранированные поля и многострочные значения сломают наивный разделитель — в таких случаях используйте PapaParse или csv-parse. Для небольших файлов, обрабатываемых в браузере, этот подход без зависимостей идеален.

JavaScript
const csv = `name,email,department
Алексей Иванов,ivanov@nexuslabs.io,Engineering
Мария Соколова,sokolova@nexuslabs.io,Product`

const lines = csv.trim().split('\n')
const headers = lines[0].split(',')
const rows = lines.slice(1).map(line => {
  const values = line.split(',')
  return Object.fromEntries(headers.map((h, i) => [h.trim(), values[i]?.trim()]))
})

console.log(JSON.stringify(rows, null, 2))
// [
//   { "name": "Алексей Иванов", "email": "ivanov@nexuslabs.io", "department": "Engineering" },
//   { "name": "Мария Соколова", "email": "sokolova@nexuslabs.io", "department": "Product" }
// ]

В чём разница между JSON.stringify() и toString() для объектов?

Вызов toString() на обычном объекте возвращает бесполезную строку "[object Object]" — она ничего не говорит о реальных данных. JSON.stringify() создаёт валидную JSON-строку со всеми ключами и значениями, правильно сериализованными, включая вложенные объекты и массивы. Всегда используйте JSON.stringify(), когда нужно преобразовать JavaScript-объект (например, строку из CSV) в JSON-строку. Для обратной операции — восстановления живых JavaScript-объектов из JSON-строки — используйте JSON.parse(), который является точной противоположностью JSON.stringify(). Эти две функции образуют полный цикл: stringify для сохранения или передачи данных, parse для их потребления.

JavaScript
const row = { name: 'Алексей Иванов', role: 'Engineer' }

console.log(row.toString())       // "[object Object]"
console.log(JSON.stringify(row))   // '{"name":"Алексей Иванов","role":"Engineer"}'

Как обрабатывать поля CSV, содержащие запятые или кавычки?

RFC 4180 указывает, что поля, содержащие запятые, переносы строк или двойные кавычки, должны быть заключены в двойные кавычки, а встроенные кавычки экранируются удвоением ("" внутри экранированного поля означает одну кавычку). Ручной split(",") ломается на таких файлах — граница поля больше не является простой запятой. Используйте PapaParse или csv-parse для производственных данных, или напишите конечный автомат-парсер, отслеживающий, находитесь ли вы внутри экранированного поля. Написать корректный конечный автомат с нуля на удивление сложно: нужно обрабатывать кавычки в начале поля, экранированные кавычки внутри поля, переносы строк внутри экранированных полей и разные соглашения о концах строк (CRLF vs LF). Для любых данных сложнее тестовых — используйте проверенную библиотеку.

JavaScript
// PapaParse корректно обрабатывает RFC 4180
import Papa from 'papaparse'

const csv = `product,description,price
"Widget, Large","A big ""widget""",29.99
Bolt,Standard bolt,1.50`

const { data } = Papa.parse(csv, { header: true })
console.log(JSON.stringify(data, null, 2))
// поле description корректно содержит: A big "widget"

Можно ли конвертировать CSV в JSON прямо в браузере?

Да. Оба метода — JSON.stringify() и JSON.parse() — встроены в каждый браузерный движок. Для шага разбора CSV можно разбивать по переносу строк и запятым для простых файлов или подключить PapaParse через CDN (у него нет зависимостей от Node.js). Вся конвертация происходит на стороне клиента без обращения к серверу, что полезно для конфиденциальности файлов — сырые CSV-данные никогда не покидают машину пользователя. Когда пользователь загружает CSV-файл через элемент <input type="file">, метод file.text() из File API возвращает Promise, который разрешается в содержимое файла в виде строки, которую затем можно передать в функцию конвертации. Это делает полностью браузерную конвертацию CSV в JSON практичной для дашбордов, инструментов разработчика и любых приложений, обрабатывающих загруженные данные без бэкенда.

JavaScript
// Браузер: пользователь загружает CSV-файл
const fileInput = document.querySelector('input[type="file"]')
fileInput.addEventListener('change', async (event) => {
  const file = event.target.files[0]
  const text = await file.text()
  const lines = text.trim().split('\n')
  const headers = lines[0].split(',')
  const rows = lines.slice(1).map(line =>
    Object.fromEntries(headers.map((h, i) => [h.trim(), line.split(',')[i]?.trim()]))
  )
  console.log(JSON.stringify(rows, null, 2))
})

Как обрабатывать числовые и булевы значения из CSV вместо того, чтобы хранить всё как строки?

В CSV нет системы типов — каждое поле является строкой. Вы должны приводить значения к нужным типам на этапе преобразования. Проверяйте числовые шаблоны с помощью Number() или parseFloat(), конвертируйте "true"/"false" в булевы значения и обрабатывайте пустые строки как null. Будьте осторожны с полями, которые выглядят как числа, но должны оставаться строками: почтовые индексы вроде "07302" теряют ведущий ноль при приведении с помощью Number(), и телефонные номера или коды товаров с числовыми символами так же уязвимы. Библиотека csvtojson выполняет автоматическое приведение типов через опцию colParser, которая позволяет задавать функции преобразования для каждого столбца и переопределять автодетекцию для проблемных столбцов. Опция dynamicTyping в PapaParse применяет то же приведение глобально ко всем столбцам.

JavaScript
function coerceValue(val) {
  if (val === '') return null
  if (val === 'true') return true
  if (val === 'false') return false
  const num = Number(val)
  if (!isNaN(num) && val.trim() !== '') return num
  return val
}

// Применяется при преобразовании CSV в объекты
const row = { port: coerceValue('8443'), debug: coerceValue('true'), host: coerceValue('api.internal') }
// { port: 8443, debug: true, host: "api.internal" }

Как записать результат CSV-в-JSON в файл в Node.js?

Используйте fs.writeFileSync() с результатом JSON.stringify(). Третий аргумент JSON.stringify управляет отступом — передайте 2 для двух пробелов или "\t" для табуляции. Чтобы прочитать файл обратно, используйте JSON.parse(fs.readFileSync(path, "utf8")), который восстанавливает живой JavaScript-массив объектов. Если вы записываете файл в асинхронном контексте (внутри async-функции или на верхнем уровне ES-модуля), предпочтительнее fs.promises.writeFile() — чтобы не блокировать цикл событий во время записи. Для больших JSON-файлов объёмом несколько мегабайт рассмотрите передачу JSON-потока в WriteStream вместо построения всей строки в памяти перед записью.

JavaScript
import { writeFileSync, readFileSync } from 'node:fs'

// Запись
const jsonOutput = JSON.stringify(rows, null, 2)
writeFileSync('employees.json', jsonOutput, 'utf8')

// Чтение обратно
const parsed = JSON.parse(readFileSync('employees.json', 'utf8'))
console.log(Array.isArray(parsed))  // true
console.log(parsed[0].name)         // "Алексей Иванов"

Связанные инструменты

Также доступно на:Python
AC
Alex ChenFront-end & Node.js Developer

Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.

SL
Sophie LaurentТехнический рецензент

Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.