CSV to JSON JavaScript — Converter + Code Examples

·Front-end & Node.js Developer·Sprawdzono przezSophie Laurent·Opublikowano

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.

Before · json
After · json
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:

JavaScript — minimalna funkcja 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" }
// ]

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:

JavaScript — csvToJson z konwersją 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ść.

Note:Surowy ciąg CSV to nie JSON. Wywołanie 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

JavaScript — Map do JSON przez Object.fromEntries()
// 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.

JavaScript — toJSON() dla niestandardowej serializacji
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 ISO

Reviver 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:

JavaScript — reviver do odtwarzania obiektów Date
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())    // 2024
Warning:JSON.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.

Parametr
Typ
Domyślnie
Opis
value
any
(wymagany)
Wartość JavaScript do serializacji — obiekt, tablica, ciąg znaków, liczba, wartość logiczna lub null
replacer
Function | Array | null
undefined
Filtruje lub przekształca wartości podczas serializacji. Tablica zawiera dozwolone nazwy właściwości; funkcja przyjmuje pary (key, value)
space
number | string
undefined
Wcięcie: liczba 0–10 dla spacji, ciąg znaków (np. "\t") dla niestandardowego wcięcia. Pomiń, aby uzyskać kompaktowe wyjście w jednej linii

Parametry JSON.parse():

Parametr
Typ
Domyślnie
Opis
text
string
(wymagany)
Ciąg JSON do sparsowania — musi być prawidłowym JSON-em, w przeciwnym razie zostanie zgłoszony SyntaxError
reviver
Function | undefined
undefined
Wywoływany dla każdej pary klucz-wartość. Zwrócona wartość zastępuje oryginał; zwrócenie undefined usuwa właściwość

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.

JavaScript — parsowanie wyjścia JSON i zapytania o wiersze
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)     // 3

Bezpieczna 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:

JavaScript — bezpieczna otoczka parse
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ą.

JavaScript — reviver do zmiany nazw kluczy i walidacji kształtu
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

Node.js 18+ — konwersja pliku
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

Node.js 18+ — konwersja odpowiedzi 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)
}
Note:Argument replacer w 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.

bash — jednoliniowiec Node.js
# 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));
"
bash — Miller (mlr) dla CSV do JSON
# 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
bash — csvtojson CLI
# 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.

Note: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.

bash — instalacja PapaParse
npm install papaparse
JavaScript — PapaParse z konwersją typów
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.

bash — instalacja cli-highlight
npm install cli-highlight
JavaScript — kolorowe wyjście JSON w terminalu
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 kolory

Kolorowe 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.

Warning:Podświetlanie składni dodaje kody ucieczki ANSI do wyjścia. Nie używaj go podczas zapisywania JSON do pliku, przekazywania do innego programu lub wysyłania jako treści odpowiedzi API — kody ucieczki spowodują uszkodzenie JSON-a. Używaj podświetlania wyłącznie do wyświetlania w terminalu.

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).

Node.js 18+ — strumieniowanie CSV do NDJSON
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.

Node.js — strumieniowanie dużego pliku przez 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) {
    // 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)
  },
})
Note:Przełącz się na strumieniowanie, gdy plik CSV przekracza 50 MB lub gdy przetwarzasz nieograniczone wejście (strumień WebSocket, zdarzenia wysyłane przez serwer, przekazywany stdin). Format NDJSON (jeden obiekt JSON na linię) jest często lepszym formatem wyjściowym niż gigantyczna tablica JSON dla dużych zbiorów danych — może być przetwarzany linia po linii bez ładowania całego pliku do pamięci.

Częste błędy

Wywołanie JSON.parse() bezpośrednio na ciągu CSV

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.

Before · JavaScript
After · JavaScript
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 JSON
Używanie toString() zamiast JSON.stringify()

Problem: 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.

Before · JavaScript
After · JavaScript
const row = { server: 'api-gateway', port: 8080 }
const output = row.toString()
// "[object Object]"  — dane utracone
const row = { server: 'api-gateway', port: 8080 }
const output = JSON.stringify(row, null, 2)
// '{"server":"api-gateway","port":8080}'
Podział po przecinku bez obsługi pól w cudzysłowie

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.

Before · JavaScript
After · JavaScript
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łowo
import 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 sparsowane
Zapominanie, że wszystkie wartości CSV są ciągami znaków

Problem: 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.

Before · JavaScript
After · JavaScript
const row = { port: '8443', debug: 'true', workers: '4' }
JSON.stringify(row)
// {"port":"8443","debug":"true","workers":"4"}  — same ciągi znaków
const row = {
  port: Number('8443'),           // 8443
  debug: 'true' === 'true',      // true
  workers: Number('4'),           // 4
}
JSON.stringify(row)
// {"port":8443,"debug":true,"workers":4}  — poprawne typy

Ręczne parsowanie vs biblioteki — krótkie porównanie

Metoda
Sformatowane wyjście
Prawidłowy JSON
Własne typy
Strumieniowanie
Wymaga instalacji
JSON.stringify()
✓ (z parametrem space)
✓ przez toJSON()/replacer
Nie (wbudowana)
JSON.parse()
N/A (parsowanie)
✓ (waliduje)
✓ przez reviver
Nie (wbudowana)
csv-parse (Node.js)
✗ (zwraca obiekty)
✗ (nie JSON)
npm install
PapaParse
✗ (zwraca obiekty)
✗ (nie JSON)
npm install
csvtojson
✓ przez colParser
npm install
jq (CLI)
N/A
Instalacja systemowa
Miller (CLI)
N/A
Instalacja systemowa

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.

JavaScript
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.

JavaScript
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.

JavaScript
// 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.

JavaScript
// 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.

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
}

// 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.

JavaScript
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

Dostępne również w: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 LaurentRecenzent techniczny

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.