CSV to JSON JavaScript — Converter + Code Examples
Використовуйте безкоштовний CSV to JSON прямо в браузері — без встановлення.
Спробувати CSV to JSON онлайн →Більшість CSV-даних надходить у вигляді плаского рядка із завантаженого файлу, експорту бази даних або API, що досі використовує формат 1970-х. Щоб конвертувати 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). Ця конвертація є першим кроком майже у кожному конвеєрі даних, що починається з експорту таблиці, дампа застарілої системи або файлу, завантаженого користувачем. Самі дані залишаються незмінними; формат змінюється з позиційних стовпців на іменовані властивості.
name,email,role,active Олександр Коваленко,o.kovalenko@nexuslabs.io,Engineering Lead,true Марія Шевченко,m.shevchenko@nexuslabs.io,Product Manager,true
[
{
"name": "Олександр Коваленко",
"email": "o.kovalenko@nexuslabs.io",
"role": "Engineering Lead",
"active": "true"
},
{
"name": "Марія Шевченко",
"email": "m.shevchenko@nexuslabs.io",
"role": "Product Manager",
"active": "true"
}
]csvToJson() — Побудова функції конвертації для повторного використання
Повний конвеєр CSV→JSON у JavaScript розбивається на три кроки: розбийте CSV-рядок по символу нового рядка для отримання рядків, витягніть заголовки з першого рядка за допомогою split(','), а потім перетворіть кожен наступний рядок на простий JavaScript-об'єкт, де ключі беруться із заголовків, а значення — з відповідних позицій стовпців. Фінальний виклик JSON.stringify() перетворює масив об'єктів у JSON-рядок. Ось мінімальна робоча версія:
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-виводі, доведеться приводити їх самостійно. Ось розширена версія з визначенням типів:
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, де пробільні символи лише витрачають пропускну здатність.
JSON.parse() безпосередньо на CSV-тексті викидає SyntaxError. Спочатку потрібно перетворити CSV на JavaScript-об'єкти за допомогою функції csvToJson(), яка всередині викликає JSON.stringify() для отримання фактичного JSON-рядка.Обробка Map, Date та власних об'єктів із 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
// Побудова 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 серіалізуються коректно, бо Date має вбудований метод toJSON(), що повертає рядок ISO 8601. Також можна визначити власний toJSON() у власних класах для контролю того, які стовпці CSV з'являться в серіалізованому виводі — наприклад, опускаючи внутрішні поля відстеження на кшталт _rowIndex.
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() {
// Експортуємо лише поля, потрібні у JSON-виводі
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 є числом, дата у форматі ISOReviver для десеріалізації дат
Після запису JSON, отриманого з CSV, у файл або відправки через мережу, JSON.parse() повертає прості об'єкти — об'єкти Date знову стають рядками. Використовуйте функцію reviver для перетворення рядків ISO 8601 назад на об'єкти 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()) // 2024JSON.stringify() повертає undefined (а не рядок) для значень, що містять функції, Symbols або властивості зі значенням undefined. Якщо ваші об'єкти, отримані з CSV, випадково отримали значення undefined від відсутнього стовпця, ця властивість непомітно зникне з JSON-виводу. Завжди замінюйте відсутні значення на null.Довідка параметрів JSON.stringify()
Сигнатура функції: JSON.stringify(value, replacer?, space?). Аргументи replacer та space є необов'язковими — передавайте null для replacer, коли потрібен лише відступ.
Параметри JSON.parse():
JSON.parse() — Споживання JSON-виводу
Отримавши JSON-рядок від csvToJson(), наступним кроком зазвичай є його розбір назад на живий JavaScript-масив для фільтрації, відображення або передачі до API. Різниця між JSON-рядком (typeof === "string") та JavaScript-об'єктом має значення. Не можна викликати .filter() або звернутися до [0].name у рядка — спочатку потрібен JSON.parse(). Цей цикл (stringify, потім parse) також працює як техніка валідації: якщо конвертація CSV з якоїсь причини сформувала некоректний JSON, parse викине помилку. Необов'язковий аргумент reviver дозволяє перетворювати кожну пару ключ-значення під час розбору — корисно для відновлення об'єктів Date з ISO-рядків або перейменування ключів без окремого проходу.
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 (обрізане введення, помилки кодування), це перехопить помилку без аварійного завершення:
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 conversion produced invalid JSON:', result.error)
} else {
console.log(`Parsed ${result.data.length} rows`)
}
// Випадкова передача необробленого CSV у JSON.parse — це не спрацює
const bad = safeParse('name,email\nОлександр,o.kovalenko@nexuslabs.io')
console.log(bad.error) // "Unexpected token 'О', "name,email"... is not valid JSON"Reviver для перейменування ключів та валідації
Функція reviver отримує кожну пару ключ-значення під час розбору, від найглибших властивостей до зовнішніх. Повернення undefined для ключа повністю видаляє його з результату; повернення іншого значення замінює оригінальне. Reviver корисний для перейменування заголовків (camelCase у snake_case), видалення внутрішніх полів або перевірки наявності обов'язкових стовпців. Він викликається з кореневим значенням останнім (порожній рядок як ключ) — там і слід кидати помилку, якщо результат не є масивом.
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
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(`Converted ${rows.length} rows → ${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
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)
}JSON.stringify() дозволяє задати білий список конкретних стовпців з CSV. Передайте масив імен заголовків, щоб включити лише ці поля: JSON.stringify(rows, ['name', 'email', 'department']). Властивості, не зазначені в масиві, непомітно виключаються з виводу.Конвертація CSV у JSON через командний рядок
Node.js може виконувати однорядкові скрипти, а також існують спеціалізовані CLI-інструменти для конвертації CSV у JSON без написання скрипту.
# Передаємо 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));
"# 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
# Глобальне встановлення 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, що дозволяє уникнути двокрокового конвеєру parse-потім-transform. csvtojson, з іншого боку, працює на Node.js і зручніший, коли решта інструментального ланцюжка написана на JavaScript: можна передавати його вивід безпосередньо у потоки Node, імпортувати як бібліотеку або використовувати його API colParser для приведення типів у коді. Надавайте перевагу Miller для максимальної пропускної здатності та shell-конвеєрів; надавайте перевагу csvtojson, коли потрібна тісна інтеграція з Node.js-застосунком.
jq не розбирає CSV нативно. Якщо потрібен jq у конвеєрі, спочатку конвертуйте у JSON за допомогою csvtojson або mlr, а потім передавайте JSON-вивід до jq для фільтрації та трансформації.Альтернатива для продуктивності — PapaParse
Ручний підхід з split(',') не справляється з реальними CSV-файлами. Поля у лапках, що містять коми, вбудовані символи нового рядка, екрановані подвійні лапки — все це ламає наївний розбивач. PapaParse — це бібліотека, яку варто використовувати, коли CSV надходить з невідомого джерела. Вона обробляє всі граничні випадки RFC 4180, автоматично визначає розділювачі та працює як у Node.js, так і в браузерах.
npm install 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(`Parsed ${data.length} rows, delimiter: "${meta.delimiter}"`)Опція dynamicTyping PapaParse виконує приведення типів автоматично — числа стають числами, "true"/"false" стають булевими значеннями. Зворотний виклик transformHeader нормалізує імена стовпців до snake_case, що позбавляє від роботи з непослідовними заголовками з різних CSV-експортів. Для швидких конвертацій без написання коду розбору конвертер CSV у JSON робить це все у браузері.
Вивід у терміналі з підсвічуванням синтаксису
Виведення великого JSON-масиву у термінал швидко стомлює очі. Додавання підсвічування синтаксису робить вивід читабельним під час налагодження та розробки. Пакет cli-highlight забарвлює JSON-вивід у терміналах Node.js.
npm install cli-highlight
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 для виявлення одного поля з помилковим типом означає читання кожного значення окремо. З кольором рядок замість числа виділяється одразу.
Робота з великими CSV-файлами
Завантаження 500 МБ CSV-файлу у рядок за допомогою 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).
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('Streaming conversion complete')
// Кожен рядок вихідного файлу є одним 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, що спрацьовує один раз на рядок замість накопичення всіх рядків у пам'яті. Передайте ReadStream Node.js (у Node.js) або об'єкт File (у браузері), і PapaParse обробляє розбивку на частини внутрішньо. Жодного потокового конвеєру не потрібно — лише зворотний виклик. Використовуйте його, коли потрібна відповідність RFC 4180 без підключення csv-parse.
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(`Row ${rowCount}: ${result.data.sku} is out of stock`)
}
},
complete() {
console.log(`Processed ${rowCount} rows`)
if (errors.length > 0) {
console.log(`Issues found: ${errors.length}`)
errors.forEach(e => console.log(` ${e}`))
}
},
error(err) {
console.error('Parse failed:', err.message)
},
})Типові помилки
Проблема: CSV — це не JSON. Передача необробленого CSV-рядка у JSON.parse() викидає SyntaxError, бо коми та символи нового рядка не є валідним синтаксисом JSON.
Рішення: Спочатку розбийте CSV у JavaScript-об'єкти за допомогою split() або бібліотеки, а потім використовуйте JSON.stringify() для отримання JSON. Викликайте JSON.parse() лише для рядків, що вже є валідним JSON.
const csv = 'name,email\nОлександр Коваленко,o.kovalenko@nexuslabs.io' const data = JSON.parse(csv) // SyntaxError: Unexpected token 'О'
const csv = 'name,email\nОлександр Коваленко,o.kovalenko@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() для JavaScript-об'єкта повертає марний рядок '[object Object]' замість реальних даних. Це непомітно знищує вивід конвертації CSV у JSON.
Рішення: Завжди використовуйте JSON.stringify() для перетворення JavaScript-об'єктів у JSON-рядки. toString() існує для приведення примітивів до рядка, а не для серіалізації.
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-даних, що ви не повністю контролюєте. Якщо необхідно розбирати вручну, реалізуйте парсер зі скінченним автоматом, що відстежує, чи поточна позиція знаходиться всередині поля у лапках.
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 значення, розбито коректноПроблема: Без приведення типів port: "8080" залишається рядком у JSON замість числа. Подальші системи, що очікують числові типи, відхиляють або неправильно обробляють дані.
Рішення: Застосовуйте явне приведення типів під час кроку відображення, або використовуйте PapaParse з dynamicTyping: true. Завжди чітко визначайте, які поля мають бути числовими.
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} — правильні типиРучний розбір проти бібліотек — швидке порівняння
Для швидких скриптів, де ви контролюєте формат 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. Для дуже маленьких файлів, що обробляються у браузері, цей підхід без залежностей є ідеальним.
const csv = `name,email,department
Олександр Коваленко,o.kovalenko@nexuslabs.io,Engineering
Марія Шевченко,m.shevchenko@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": "o.kovalenko@nexuslabs.io", "department": "Engineering" },
// { "name": "Марія Шевченко", "email": "m.shevchenko@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 для їх споживання.
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). Для будь-яких даних складніших за найпростіший CSV використовуйте добре перевірену бібліотеку.
// 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 field correctly contains: 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 практичною для дашбордів, інструментів розробника та будь-яких застосунків, що обробляють завантажені дані без бекенду.
// Браузер: користувач завантажує 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 застосовує таке ж приведення глобально для всіх стовпців.
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 замість того, щоб будувати весь рядок у пам'яті перед записом.
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) // "Олександр Коваленко"Пов'язані інструменти
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.
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.