CSV to JSON JavaScript — Converter + Code Examples
Użyj darmowego CSV do JSON bezpośrednio w przeglądarce — bez instalacji.
Wypróbuj CSV do JSON online →Większość danych CSV, z którymi się spotykam, dociera jako płaski ciąg znaków z przesyłanego pliku, eksportu bazy danych lub API, które wciąż mówi w formacie z lat 70. Aby przekonwertować CSV do JSON w JavaScript, potrzebujesz dwóch rzeczy, które język zapewnia bezpłatnie: podziału ciągu do parsowania wierszy oraz JSON.stringify() do serializacji wyniku. Żadne pakiety npm nie są wymagane do podstawowego użycia — ten przewodnik obejmuje pełny potok od wielokrotnie używanej funkcji csvToJson() przez PapaParse aż po operacje wejścia/wyjścia plików w Node.js. Do szybkich konwersji bez kodu konwerter CSV do JSON online obsługuje je natychmiast. Wszystkie przykłady są przeznaczone dla Node.js 18+ i nowoczesnych przeglądarek.
- ✓Podziel CSV po znaku nowej linii, wyodrębnij nagłówki z wiersza 0, odwzoruj pozostałe wiersze na obiekty, a następnie użyj JSON.stringify(array, null, 2) dla czytelnego wyjścia.
- ✓JSON.stringify() tworzy ciąg znaków; JSON.parse() konwertuje go z powrotem na aktywną tablicę JavaScript — wiedz, z czym masz do czynienia, zanim zaczniesz na tym operować.
- ✓Instancje Map nie serializują się do JSON automatycznie — najpierw wywołaj Object.fromEntries(map).
- ✓W przypadku CSV z polami w cudzysłowie, przecinkami wewnątrz wartości lub znakami nowej linii w komórkach użyj PapaParse lub csv-parse zamiast ręcznego podziału.
- ✓csvtojson (npm) obsługuje konwersję typów, strumieniowanie i przypadki skrajne RFC 4180 w jednym wywołaniu.
Czym jest konwersja CSV do JSON?
Konwersja CSV do JSON przekształca płaski, rozdzielany przecinkami format tekstowy w strukturalną tablicę obiektów, gdzie każdy wiersz staje się obiektem JavaScript z kluczami odpowiadającymi nagłówkom kolumn. Format CSV nie ma typów danych — wszystko jest ciągiem znaków. JSON dodaje strukturę, zagnieżdżanie i jawne typy (liczby, wartości logiczne, null). Ta konwersja jest pierwszym krokiem w niemal każdym potoku danych, który zaczyna się od eksportu arkusza kalkulacyjnego, zrzutu ze starszego systemu lub pliku przesyłanego przez użytkownika. Dane źródłowe pozostają takie same; format zmienia się z kolumn opartych na pozycji na właściwości nazwane.
name,email,role,active Piotr Kowalski,pkowalski@nexuslabs.io,Engineering Lead,true Anna Nowak,anowak@nexuslabs.io,Product Manager,true
[
{
"name": "Piotr Kowalski",
"email": "pkowalski@nexuslabs.io",
"role": "Engineering Lead",
"active": "true"
},
{
"name": "Anna Nowak",
"email": "anowak@nexuslabs.io",
"role": "Product Manager",
"active": "true"
}
]csvToJson() — budowanie wielokrotnie używanej funkcji konwersji
Pełny potok CSV do JSON w JavaScript składa się z trzech kroków: podziel ciąg CSV po znaku nowej linii, aby uzyskać wiersze, wyodrębnij nagłówki z pierwszego wiersza za pomocą split(','), a następnie odwzoruj każdy pozostały wiersz na zwykły obiekt JavaScript, gdzie klucze pochodzą z nagłówków, a wartości z odpowiednich pozycji kolumn. Ostatnie wywołanie JSON.stringify() konwertuje tę tablicę obiektów na ciąg JSON. Oto minimalna działająca wersja:
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" }
// ]Ta funkcja obsługuje podstawowe przypadki: końcowe znaki nowej linii, puste linie, białe znaki wokół wartości. Każde pole CSV przechodzi jako ciąg znaków. Zauważ, że port to "8080" (ciąg znaków), nie 8080 (liczba). Jeśli potrzebujesz właściwych typów w wyjściu JSON, musisz je samodzielnie konwertować. Oto rozszerzona wersja z wykrywaniem typów:
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 }
// ]Flaga coerce jest opcjonalna, ponieważ automatyczne wykrywanie typów może przynieść odwrotny skutek — pole takie jak kod pocztowy ("07302") traci wiodące zero po konwersji na liczbę. Pozostaw konwersję wyłączoną domyślnie i włączaj ją tylko wtedy, gdy kontrolujesz schemat. Krótka uwaga: JSON.stringify() przyjmuje trzeci argument space dla wcięcia. Przekaż 2 dla dwóch spacji, 4 dla czterech lub "\t" dla tabulatorów. Pomiń ten argument całkowicie dla kompaktowego wyjścia w jednej linii — przydatne przy wysyłaniu ciągu JSON jako treści żądania API, gdzie białe znaki tylko marnują przepustowość.
JSON.parse() bezpośrednio na tekście CSV zgłasza SyntaxError. Najpierw musisz przekonwertować CSV na obiekty JavaScript za pomocą funkcji csvToJson(), która wewnętrznie wywołuje JSON.stringify(), aby wyprodukować właściwy ciąg JSON.Obsługa Map, dat i niestandardowych obiektów z danych CSV
Nie każda konwersja CSV kończy się na płaskiej tablicy zwykłych obiektów. Czasami trzeba zbudować Map z par nagłówek-wartość, sparsować ciągi dat na obiekty Date lub dołączyć właściwości obliczeniowe przed serializacją. JavaScript ma pewną osobliwość, która często zaskakuje: instancje Map nie serializują się za pomocą JSON.stringify(). Otrzymujesz pusty obiekt. Rozwiązaniem jest Object.fromEntries() do konwersji Map z powrotem na zwykły obiekt przed serializacją.
Powodem, dla którego Map serializuje się do {} jest to, że JSON.stringify() iteruje po własnych wyliczalnych właściwościach obiektu. Map przechowuje swoje wpisy w wewnętrznym slocie, nie jako wyliczalne właściwości na samym obiekcie, więc serializator widzi obiekt bez kluczy. Prototyp Map nie posiada też metody toJSON(), która jest punktem zaczepienia, który JSON.stringify() wywołuje jako pierwszy na każdej wartości przed podjęciem decyzji o sposobie jej serializacji. Jeśli wartość posiada metodę toJSON(), to jej wartość zwracana jest tym, co zostaje zserializowane — nie sam obiekt. Dlatego obiekty Date serializują się poprawnie: Date.prototype.toJSON zwraca ciąg ISO 8601, więc JSON.stringify(new Date()) tworzy cytowany znacznik czasu zamiast pustego obiektu. Rozumienie tego mechanizmu pozwala zdefiniować takie samo zachowanie we własnych klasach — jak pokazano w przykładzie EmployeeRecord poniżej — aby kontrolować dokładnie, które pola wyprowadzone z CSV pojawią się w końcowym wyjściu JSON.
Konwersja Map do JSON
// Zbuduj Map z par nagłówek→wartość z 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 NIE serializuje się bezpośrednio
console.log(JSON.stringify(rowMap))
// "{}" — pusty obiekt, dane utracone!
// Najpierw przekonwertuj na zwykły obiekt
const rowObj = Object.fromEntries(rowMap)
console.log(JSON.stringify(rowObj, null, 2))
// {
// "server": "payments-api",
// "port": "9090",
// "region": "ap-south-1"
// }Ciągi dat i metoda toJSON()
Pola dat w CSV docierają jako ciągi znaków. Jeśli parsowanie ich do obiektów Date odbywa się podczas przetwarzania, te obiekty Date serializują się poprawnie, ponieważ Date posiada wbudowaną metodę toJSON(), która zwraca ciąg ISO 8601. Możesz też zdefiniować własną metodę toJSON() we własnych klasach, aby kontrolować, które kolumny CSV pojawią się w serializowanym wyjściu — na przykład pomijając wewnętrzne pola śledzące, takie jak _rowIndex.
class EmployeeRecord {
constructor(csvRow) {
this._rowIndex = csvRow._rowIndex // wewnętrzne, nie dla JSON
this.employeeId = csvRow.employee_id
this.name = csvRow.name
this.hiredAt = new Date(csvRow.hired_date)
this.salary = Number(csvRow.salary)
}
toJSON() {
// Ujawniaj tylko pola, które chcemy w wyjściu JSON
return {
employee_id: this.employeeId,
name: this.name,
hired_at: this.hiredAt, // Date.toJSON() → ciąg ISO automatycznie
salary: this.salary,
}
}
}
const csvRow = {
_rowIndex: 42,
employee_id: 'EMP-2847',
name: 'Piotr Kowalski',
hired_date: '2024-03-15',
salary: '128000',
}
const record = new EmployeeRecord(csvRow)
console.log(JSON.stringify(record, null, 2))
// {
// "employee_id": "EMP-2847",
// "name": "Piotr Kowalski",
// "hired_at": "2024-03-15T00:00:00.000Z",
// "salary": 128000
// }
// Uwaga: _rowIndex jest wykluczone, salary jest liczbą, data jest w formacie ISOReviver do deserializacji dat
Po zapisaniu JSON wyprowadzonego z CSV do pliku lub przesłaniu go przez sieć, JSON.parse() zwraca z powrotem zwykłe obiekty — obiekty Date stają się ponownie ciągami znaków. Użyj funkcji reviver, aby przekonwertować ciągi ISO 8601 z powrotem na obiekty Date podczas parsowania:
const jsonString = '{"employee_id":"EMP-2847","name":"Piotr Kowalski","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() zwraca undefined (nie ciąg znaków) dla wartości zawierających funkcje, Symbole lub właściwości undefined. Jeśli obiekty wyprowadzone z CSV przypadkowo pobiorą wartość undefined z brakującej kolumny, ta właściwość po cichu znika z wyjścia JSON. Zawsze używaj null jako wartości domyślnej dla brakujących wartości.Dokumentacja parametrów JSON.stringify()
Sygnatura funkcji to JSON.stringify(value, replacer?, space?). Argumenty replacer i space są opcjonalne — przekaż null dla replacera, gdy potrzebujesz tylko wcięcia.
Parametry JSON.parse():
JSON.parse() — konsumowanie wyjścia JSON
Gdy już masz ciąg JSON z csvToJson(), następnym krokiem jest zazwyczaj sparsowanie go z powrotem na aktywną tablicę JavaScript do filtrowania, odwzorowania lub przekazania do API. Różnica między ciągiem JSON (typeof === "string") a obiektem JavaScript ma znaczenie. Nie możesz wywołać .filter() ani uzyskać dostępu do [0].name na ciągu znaków — potrzebujesz najpierw JSON.parse(). Ta pętla (stringify, a następnie parse) działa też jako technika walidacji: jeśli konwersja CSV wyprodukuje coś, co nie jest prawidłowym JSON-em, parse zgłosi wyjątek. Opcjonalny argument reviver pozwala przekształcać każdą parę klucz-wartość podczas parsowania — przydatny do przywracania obiektów Date z ciągów ISO lub zmiany nazw kluczy bez oddzielnego przejścia.
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`
// Krok 1: przekonwertuj CSV na ciąg JSON
const jsonString = csvToJson(csv, { coerce: true })
// Krok 2: sparsuj z powrotem na tablicę JavaScript
const endpoints = JSON.parse(jsonString)
// Sprawdź, czy to tablica
console.log(Array.isArray(endpoints)) // true
// Filtruj endpointy o wysokim opóźnieniu
const slow = endpoints.filter(ep => ep.avg_latency_ms > 200)
console.log(slow.map(ep => ep.endpoint))
// ["/api/v2/orders", "/api/v2/payments"]
// Destrukturyzuj pierwszy wiersz
const [first, ...rest] = endpoints
console.log(first.endpoint) // "/api/v2/orders"
console.log(rest.length) // 3Bezpieczna otoczka dla JSON.parse() jest przydatna przy walidacji wyjścia konwersji przed dalszym przetwarzaniem. Jeśli konwersja CSV z jakiegoś powodu wyprodukuje zniekształcony JSON (obcięte wejście, błędy kodowania), pozwala to przechwycić błąd bez awarii:
function safeParse(jsonString) {
try {
return { data: JSON.parse(jsonString), error: null }
} catch (err) {
return { data: null, error: err.message }
}
}
// Prawidłowe wyjście
const result = safeParse(csvToJson(csv))
if (result.error) {
console.error('Konwersja CSV produkowała nieprawidłowy JSON:', result.error)
} else {
console.log(`Sparsowano ${result.data.length} wierszy`)
}
// Przypadkowe przekazanie surowego CSV do JSON.parse — to się nie powiedzie
const bad = safeParse('name,email\nPiotr,pkowalski@nexuslabs.io')
console.log(bad.error) // "Unexpected token 'a', "name,email"... is not valid JSON"Reviver do zmiany nazw kluczy i walidacji
Funkcja reviver otrzymuje każdą parę klucz-wartość podczas parsowania, od właściwości najbardziej wewnętrznych na zewnątrz. Zwrócenie undefined dla klucza całkowicie usuwa go z wyniku; zwrócenie innej wartości zastępuje ją. Reviver jest przydatny do zmiany nazw nagłówków (camelCase na snake_case), usuwania wewnętrznych pól lub sprawdzania, czy wymagane kolumny istnieją. Jest wywoływany z wartością główną jako ostatni (pusty klucz ciągu znaków), gdzie rzucasz wyjątek, jeśli wynik nie jest tablicą.
const jsonString = csvToJson(`employeeId,firstName,hiredDate
EMP-2847,Piotr,2024-03-15
EMP-3012,Anna,2023-11-01`, { coerce: false })
const camelToSnake = str => str.replace(/[A-Z]/g, c => '_' + c.toLowerCase())
const employees = JSON.parse(jsonString, function(key, value) {
// Wartość główna — waliduj kształt
if (key === '') {
if (!Array.isArray(value)) throw new Error('Oczekiwano tablicy JSON z CSV')
return value
}
// Zmień nazwy kluczy nagłówków z camelCase na 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: 'Piotr', hired_date: '2024-03-15' }Konwersja CSV z pliku i odpowiedzi API
Dwa miejsca, z których faktycznie pochodzi CSV w środowisku produkcyjnym: pliki na dysku i odpowiedzi HTTP. Oba scenariusze wymagają obsługi błędów, ponieważ wejście jest zewnętrzne i niekontrolowane.
Odczyt pliku CSV, konwersja, zapis JSON
import { readFileSync, writeFileSync } from 'node:fs'
function csvToJsonFromFile(inputPath, outputPath) {
let csvText
try {
csvText = readFileSync(inputPath, 'utf8')
} catch (err) {
throw new Error(`Nie udało się odczytać ${inputPath}: ${err.message}`)
}
const lines = csvText.trim().split('\n')
if (lines.length < 2) {
throw new Error(`${inputPath} nie ma wierszy danych (tylko ${lines.length} linia)`)
}
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(`Przekonwertowano ${rows.length} wierszy → ${outputPath}`)
return rows
}
// Użycie
const data = csvToJsonFromFile('inventory.csv', 'inventory.json')
console.log(data[0])
// { sku: "WDG-2847", warehouse: "us-east-1", quantity: "150", ... }Pobieranie CSV z endpointu 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(`Nieoczekiwany 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
}
// Przykład: pobierz CSV kursów wymiany od dostawcy danych
try {
const rates = await fetchCsvAsJson('https://data.ecb.internal/rates/daily.csv')
console.log(JSON.stringify(rates.slice(0, 3), null, 2))
// Wyślij jako JSON do usługi downstream
await fetch('https://api.internal/v2/rates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: rates }),
})
} catch (err) {
console.error('Synchronizacja kursów nie powiodła się:', err.message)
}JSON.stringify() pozwala wybrać konkretne kolumny z CSV. Przekaż tablicę nazw nagłówków, aby uwzględnić tylko te pola: JSON.stringify(rows, ['name', 'email', 'department']). Właściwości spoza tablicy są po cichu wykluczane z wyjścia.Konwersja CSV do JSON z linii poleceń
Node.js może uruchamiać skrypty inline, a dostępne są też dedykowane narzędzia CLI, które obsługują konwersję CSV do JSON bez pisania skryptu.
# Przekaż CSV do skryptu inline 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 to scyzoryk szwajcarski dla danych strukturalnych # Instalacja: brew install miller (macOS) lub apt install miller (Debian/Ubuntu) mlr --icsv --ojson cat inventory.csv # Filtruj wiersze podczas konwersji mlr --icsv --ojson filter '$quantity > 100' inventory.csv # Wybierz konkretne kolumny mlr --icsv --ojson cut -f sku,warehouse,quantity inventory.csv
# Zainstaluj globalnie npm install -g csvtojson # Konwertuj plik csvtojson servers.csv > servers.json # Przekaż ze stdin cat exports/q1-metrics.csv | csvtojson > q1-metrics.json
W przypadku dużych plików Miller jest zazwyczaj lepszym wyborem niż csvtojson. Miller jest zaimplementowany w C i przetwarza CSV jako strumień bez ładowania całego pliku do pamięci, co oznacza, że obsługuje eksporty o rozmiarach gigabajtów przy stałym zużyciu pamięci. Obsługuje też operacje na poziomie pól w miejscu — zmianę nazw kolumn, konwersję typów wartości, filtrowanie wierszy — zanim dane w ogóle staną się JSON-em, dzięki czemu unikasz potoku dwa kroki: parsowanie, a następnie transformacja. csvtojson z kolei działa w Node.js i jest wygodniejszy, gdy reszta twojego zestawu narzędzi to JavaScript: możesz przekazać jego wyjście bezpośrednio do strumieni Node, zaimportować go jako bibliotekę lub użyć jego colParser API do konwersji typów per kolumna w kodzie. Preferuj Miller dla surowej przepustowości i potoków powłoki; preferuj csvtojson, gdy potrzebujesz ścisłej integracji z aplikacją Node.js.
jq nie parsuje CSV natywnie. Jeśli potrzebujesz jq w potoku, najpierw przekonwertuj do JSON za pomocą csvtojson lub mlr, a następnie przekaż wyjście JSON do jq w celu filtrowania i transformacji.Wydajna alternatywa — PapaParse
Ręczne podejście z split(',') zawodzi na rzeczywistych plikach CSV. Pola w cudzysłowie zawierające przecinki, osadzone znaki nowej linii, escapowane podwójne cudzysłowy — wszystkie te przypadki psują naiwny podział. PapaParse to biblioteka, po którą sięgam, gdy CSV pochodzi z nieznanego źródła. Obsługuje każdy przypadek skrajny RFC 4180, automatycznie wykrywa separatory i działa zarówno w Node.js, jak i w przeglądarkach.
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, // automatycznie konwertuje liczby i wartości logiczne
skipEmptyLines: true,
transformHeader: h => h.trim().toLowerCase().replace(/\s+/g, '_'),
})
if (errors.length > 0) {
console.error('Błędy parsowania:', 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(`Sparsowano ${data.length} wierszy, separator: "${meta.delimiter}"`)Opcja dynamicTyping biblioteki PapaParse wykonuje konwersję typów automatycznie — liczby stają się liczbami, "true"/"false" stają się wartościami logicznymi. Callback transformHeader normalizuje nazwy kolumn do snake_case, co oszczędza ci konieczności radzenia sobie z niespójnymi nagłówkami z różnych eksportów CSV. Do szybkich konwersji bez pisania jakiegokolwiek kodu parsowania konwerter CSV do JSON obsługuje to wszystko w przeglądarce.
Wyjście terminala z podświetlaniem składni
Zrzucanie dużej tablicy JSON do terminala bardzo szybko męczy oczy. Dodanie podświetlania składni do wyjścia sprawia, że jest ono czytelne podczas debugowania i programowania. Pakiet cli-highlight koloryzuje wyjście JSON w terminalach Node.js.
npm install cli-highlight
import { highlight } from 'cli-highlight'
// Po konwersji CSV na tablicę JSON
const jsonOutput = JSON.stringify(rows, null, 2)
// Wypisz z podświetlaniem składni
console.log(highlight(jsonOutput, { language: 'json' }))
// Klucze, ciągi znaków, liczby i wartości logiczne otrzymują odrębne koloryKolorowe wyjście jest szczególnie przydatne podczas interaktywnej inspekcji dużego wyniku konwersji. Klucze JSON, wartości ciągów, liczby i wartości logiczne otrzymują odrębne kolory ANSI, co ułatwia dostrzeżenie pola z nieprawidłowym typem — na przykład numeru portu, który powinien być 8080 liczbą, ale jest podświetlony jako ciąg znaków z powodu wyłączonej konwersji typów. Jest to szczególnie przydatne podczas debugowania plików CSV eksportowanych z narzędzi arkuszowych, gdzie typy kolumn są niespójne w wierszach. Bez koloru skanowanie 50 wierszy JSON w poszukiwaniu jednego błędnie wpisanego pola oznacza czytanie każdej wartości indywidualnie. Z kolorem ciąg znakowy wyglądający jak liczba od razu rzuca się w oczy.
Praca z dużymi plikami CSV
Ładowanie pliku CSV o rozmiarze 500 MB do ciągu znaków za pomocą readFileSync() pochłonie pamięć i potencjalnie spowoduje awarię procesu. W przypadku dużych plików przetwarzaj CSV linia po linii i emituj obiekty JSON w miarę ich napływania. Pakiet csv-parse (część ekosystemu csv na npm) dostarcza strumieniowy parser, który działa ze strumieniami Node.js.
Strumieniowanie CSV do NDJSON za pomocą csv-parse
NDJSON (Newline-Delimited JSON) to format, w którym każda linia pliku wyjściowego jest samodzielnym obiektem JSON. W przeciwieństwie do pojedynczej dużej tablicy JSON — która wymaga, aby cały plik był w pamięci przed rozpoczęciem odczytu — pliki NDJSON mogą być przetwarzane linia po linii. Sprawia to, że NDJSON jest idealny dla dużych zbiorów danych, które będą konsumowane przez procesory logów, potoki strumieniowe lub bazy danych z API masowego importu. Pakiet csv-parse emituje jeden obiekt JavaScript na wiersz CSV w trybie obiektowym, więc możesz go przekazać bezpośrednio do strumienia transformacji, który dołącza \n po każdym 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'
// Przekształć każdy obiekt wiersza CSV na linię 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, // użyj pierwszego wiersza jako nagłówków
skip_empty_lines: true,
trim: true,
cast: true, // automatycznie konwertuj liczby i wartości logiczne
}),
toNdjson,
createWriteStream('telemetry-2026-03.ndjson')
)
console.log('Strumieniowa konwersja zakończona')
// Każda linia w pliku wyjściowym to jeden obiekt 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}
// ...Strumieniowanie PapaParse dla przeglądarki i Node.js
Tryb strumieniowy PapaParse używa wywołania zwrotnego step, które uruchamia się raz na wiersz zamiast zbierać wszystkie wiersze w pamięci. Przekazujesz mu Node.js ReadStream (w Node.js) lub obiekt File (w przeglądarce), a PapaParse obsługuje podział na fragmenty wewnętrznie. Żadnego potoku strumieniowego do skonfigurowania — tylko wywołanie zwrotne. Używaj go, gdy potrzebujesz zgodności z RFC 4180 bez dołączania 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) {
// Przetwarzaj jeden wiersz na raz — stałe zużycie pamięci
rowCount++
if (result.data.quantity === 0) {
errors.push(`Wiersz ${rowCount}: ${result.data.sku} jest niedostępny`)
}
},
complete() {
console.log(`Przetworzono ${rowCount} wierszy`)
if (errors.length > 0) {
console.log(`Znalezione problemy: ${errors.length}`)
errors.forEach(e => console.log(` ${e}`))
}
},
error(err) {
console.error('Parsowanie nie powiodło się:', err.message)
},
})Częste błędy
Problem: CSV to nie JSON. Przekazanie surowego ciągu CSV do JSON.parse() zgłasza SyntaxError, ponieważ przecinki i znaki nowej linii nie są prawidłową składnią JSON.
Fix: Najpierw sparsuj CSV na obiekty JavaScript za pomocą split() lub biblioteki, a następnie użyj JSON.stringify(), aby wyprodukować JSON. Wywołuj JSON.parse() tylko na ciągach, które już są prawidłowym JSON-em.
const csv = 'name,email\nPiotr Kowalski,pkowalski@nexuslabs.io' const data = JSON.parse(csv) // SyntaxError: Unexpected token 'a'
const csv = 'name,email\nPiotr Kowalski,pkowalski@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) // prawidłowy ciąg JSONProblem: Wywołanie toString() na obiekcie JavaScript zwraca bezużyteczny ciąg '[object Object]' zamiast rzeczywistych danych. To po cichu niszczy wyjście CSV do JSON.
Fix: Zawsze używaj JSON.stringify(), aby konwertować obiekty JavaScript na ciągi JSON. toString() służy do konwersji prymityw na ciąg znaków, nie do serializacji.
const row = { server: 'api-gateway', port: 8080 }
const output = row.toString()
// "[object Object]" — dane utraconeconst row = { server: 'api-gateway', port: 8080 }
const output = JSON.stringify(row, null, 2)
// '{"server":"api-gateway","port":8080}'Problem: Naiwny split(",") psuje się, gdy wartości CSV zawierają przecinki wewnątrz pól w cudzysłowie: "Widget, Large" staje się dwiema oddzielnymi wartościami zamiast jednej.
Fix: Używaj PapaParse lub csv-parse dla danych CSV, których nie kontrolujesz w pełni. Jeśli musisz parsować ręcznie, zaimplementuj parser oparty na automacie stanów, który śledzi, czy bieżąca pozycja jest wewnątrz pola w cudzysłowie.
const line = '"Widget, Large","Premium quality",29.99'
const values = line.split(',')
// ["\"Widget", " Large\"", "\"Premium quality\"", "29.99"]
// 4 wartości zamiast 3 — pierwsze pole podzielone nieprawidłowoimport Papa from 'papaparse'
const { data } = Papa.parse('"Widget, Large","Premium quality",29.99')
// data[0] = ["Widget, Large", "Premium quality", "29.99"]
// 3 wartości, poprawnie sparsowaneProblem: Bez konwersji typów port: "8080" pozostaje ciągiem znaków w JSON zamiast stać się liczbą. Systemy downstream oczekujące typów liczbowych odrzucają lub błędnie obsługują takie dane.
Fix: Stosuj jawną konwersję typów podczas etapu odwzorowania lub użyj PapaParse z opcją dynamicTyping: true. Zawsze świadomie decyduj, które pola powinny być liczbowe.
const row = { port: '8443', debug: 'true', workers: '4' }
JSON.stringify(row)
// {"port":"8443","debug":"true","workers":"4"} — same ciągi znakówconst row = {
port: Number('8443'), // 8443
debug: 'true' === 'true', // true
workers: Number('4'), // 4
}
JSON.stringify(row)
// {"port":8443,"debug":true,"workers":4} — poprawne typyRęczne parsowanie vs biblioteki — krótkie porównanie
Do szybkich skryptów, w których kontrolujesz format CSV i wiesz, że nie ma pól w cudzysłowie, wbudowane podejście split() + JSON.stringify() działa i nie wymaga żadnych zależności. W systemach produkcyjnych przetwarzających pliki CSV przesyłane przez użytkowników używaj PapaParse w przeglądarce lub csv-parse w Node.js — oba obsługują RFC 4180 poprawnie i wspierają strumieniowanie. Pakiet csvtojson jest jedynym, który bezpośrednio wyprowadza JSON, obsługując zarówno parsowanie, jak i serializację w jednym wywołaniu. Gdy potrzebujesz najszybszej ścieżki od wklejenia do wyniku, konwerter CSV do JSON obsługuje to wszystko w przeglądarce bez żadnej konfiguracji.
Często zadawane pytania
Jak przekonwertować CSV do JSON w JavaScript bez biblioteki?
Podziel ciąg CSV po znaku nowej linii, aby uzyskać wiersze, wyodrębnij nagłówki z pierwszego wiersza za pomocą split(","), a następnie odwzoruj pozostałe wiersze na obiekty kluczowane tymi nagłówkami. Na koniec wywołaj JSON.stringify(array, null, 2), aby uzyskać sformatowany ciąg JSON. To podejście sprawdza się dobrze w przypadku prostych plików CSV, w których kontrolujesz format i wiesz, że nie ma pól w cudzysłowie ani osadzonych przecinków. W przypadku danych z eksportów arkuszy kalkulacyjnych lub systemów zewnętrznych pola w cudzysłowie i wieloliniowe wartości zepsują naiwny podział — przejdź wtedy na PapaParse lub csv-parse. Dla bardzo małych plików przetwarzanych w przeglądarce to podejście bez zależności jest idealne.
const csv = `name,email,department
Piotr Kowalski,pkowalski@nexuslabs.io,Engineering
Anna Nowak,anowak@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": "Piotr Kowalski", "email": "pkowalski@nexuslabs.io", "department": "Engineering" },
// { "name": "Anna Nowak", "email": "anowak@nexuslabs.io", "department": "Product" }
// ]Jaka jest różnica między JSON.stringify() a toString() dla obiektów?
Wywołanie toString() na zwykłym obiekcie zwraca bezużyteczny ciąg "[object Object]" — nie zawiera on żadnych informacji o rzeczywistych danych. JSON.stringify() tworzy prawidłowy ciąg JSON ze wszystkimi kluczami i wartościami poprawnie zserializowanymi, łącznie z zagnieżdżonymi obiektami i tablicami. Zawsze używaj JSON.stringify(), gdy potrzebujesz przekonwertować obiekt JavaScript (np. wiersz wyprowadzony z CSV) na ciąg JSON. Aby odwrócić tę operację — odtworzyć aktywne obiekty JavaScript z ciągu JSON — użyj JSON.parse(), który jest dokładnym odwróceniem JSON.stringify(). Te dwie funkcje tworzą kompletną pętlę: stringify do utrwalenia lub przesłania danych, parse do ich odczytania.
const row = { name: 'Piotr Kowalski', role: 'Engineer' }
console.log(row.toString()) // "[object Object]"
console.log(JSON.stringify(row)) // '{"name":"Piotr Kowalski","role":"Engineer"}'Jak obsługiwać pola CSV zawierające przecinki lub cudzysłowy?
RFC 4180 określa, że pola zawierające przecinki, znaki nowej linii lub podwójne cudzysłowy muszą być ujęte w podwójne cudzysłowy, a osadzone cudzysłowy są escapowane przez podwojenie ("" wewnątrz pola w cudzysłowie reprezentuje pojedynczy dosłowny cudzysłów). Ręczny split(",") psuje się na takich plikach — granica pola nie jest już zwykłym przecinkiem. Użyj PapaParse lub csv-parse do danych produkcyjnych albo napisz parser oparty na automacie stanów, który śledzi, czy jesteś wewnątrz pola w cudzysłowie. Napisanie poprawnego automatu stanów od zera jest zaskakująco trudne: musisz obsłużyć cudzysłowy na początku pola, escapowane cudzysłowy wewnątrz pola, znaki nowej linii wewnątrz pól w cudzysłowie oraz różne konwencje końca linii (CRLF vs LF). Dla czegokolwiek więcej niż prostych danych CSV użyj sprawdzonej biblioteki.
// PapaParse poprawnie obsługuje 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))
// pole description poprawnie zawiera: A big "widget"Czy mogę przekonwertować CSV do JSON bezpośrednio w przeglądarce?
Tak. Zarówno JSON.stringify(), jak i JSON.parse() są wbudowane w każdy silnik przeglądarki. Do etapu parsowania CSV możesz podzielić po znaku nowej linii i przecinku dla prostych plików lub dołączyć PapaParse przez CDN (nie ma on zależności od Node.js). Cała konwersja odbywa się po stronie klienta bez żadnej komunikacji z serwerem, co jest przydatne ze względu na prywatność danych — surowe dane CSV nigdy nie opuszczają maszyny użytkownika. Gdy użytkownik przesyła plik CSV przez element <input type="file">, metoda file.text() File API zwraca Promise, który rozwiązuje się do zawartości pliku jako ciągu znaków, który można następnie przekazać do funkcji konwersji. Dzięki temu konwersja CSV do JSON wyłącznie w przeglądarce jest praktyczna w przypadku dashboardów, narzędzi deweloperskich i każdej aplikacji, która musi przetwarzać przesyłane dane bez zaplecza serwerowego.
// Przeglądarka: użytkownik przesyła plik 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))
})Jak parsować wartości liczbowe i logiczne z CSV zamiast pozostawiać wszystko jako ciągi znaków?
CSV nie ma systemu typów — każde pole jest ciągiem znaków. Musisz konwertować wartości podczas etapu odwzorowania. Sprawdzaj wzorce liczbowe za pomocą Number() lub parseFloat(), konwertuj "true"/"false" na wartości logiczne i obsługuj puste ciągi jako null. Uważaj na pola, które wyglądają jak liczby, ale muszą pozostać ciągami: kody pocztowe takie jak "07302" tracą wiodące zero po konwersji za pomocą Number(), a numery telefonów lub kody produktów z cyfrowymi znakami są podobnie podatne. Biblioteka csvtojson wykonuje automatyczną konwersję typów przez opcję colParser, która pozwala określić funkcje konwersji dla poszczególnych kolumn i nadpisać automatyczne wykrywanie dla problematycznych kolumn. Opcja dynamicTyping biblioteki PapaParse stosuje tę samą konwersję globalnie do wszystkich kolumn.
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
}
// Zastosuj podczas odwzorowania CSV na obiekty
const row = { port: coerceValue('8443'), debug: coerceValue('true'), host: coerceValue('api.internal') }
// { port: 8443, debug: true, host: "api.internal" }Jak zapisać wyjście CSV do JSON do pliku w Node.js?
Użyj fs.writeFileSync() z wyjściem JSON.stringify(). Trzeci argument JSON.stringify kontroluje wcięcie — przekaż 2 dla wcięcia dwoma spacjami lub "\t" dla tabulatorów. Aby odczytać plik z powrotem, użyj JSON.parse(fs.readFileSync(path, "utf8")), co odtwarza aktywną tablicę obiektów JavaScript. Jeśli zapisujesz plik w kontekście asynchronicznym (wewnątrz funkcji async lub na najwyższym poziomie modułu ES), preferuj fs.promises.writeFile(), aby uniknąć blokowania pętli zdarzeń podczas zapisu. W przypadku dużych wyjść JSON przekraczających kilka megabajtów rozważ przesłanie strumienia JSON do WriteStream zamiast budowania całego ciągu w pamięci przed zapisem.
import { writeFileSync, readFileSync } from 'node:fs'
// Zapis
const jsonOutput = JSON.stringify(rows, null, 2)
writeFileSync('employees.json', jsonOutput, 'utf8')
// Odczyt z powrotem
const parsed = JSON.parse(readFileSync('employees.json', 'utf8'))
console.log(Array.isArray(parsed)) // true
console.log(parsed[0].name) // "Piotr Kowalski"Powiązane narzędzia
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.