Konwersja CSV na JSON: metody, pułapki i najlepsze praktyki
Zespół operacyjny przysyła eksport CSV. Aplikacja oczekuje JSON. Otwierasz plik, patrzysz na 10 000 wierszy wartości rozdzielonych przecinkami i zastanawiasz się: jak najszybciej przekonwertować CSV na JSON bez utraty danych?
Ten przewodnik obejmuje cztery metody konwersji (narzędzia w przeglądarce, JavaScript, Python, CLI), kierunek odwrotny (JSON na CSV), pięć pułapek, które po cichu psują dane, oraz sposób obsługi plików zbyt dużych, by zmieścić je w pamięci.
CSV a JSON: kiedy użyć którego
Zanim przystąpisz do konwersji, warto zrozumieć, w czym każdy z formatów jest dobry.
| Wymiar | CSV | JSON |
|---|---|---|
| Struktura | Płaska tabela (wiersze i kolumny) | Zagnieżdżona hierarchia (obiekty, tablice) |
| Typy danych | Wszystko jest łańcuchem znaków | string, number, boolean, null |
| Czytelność dla człowieka | Przyjazny dla arkuszy kalkulacyjnych | Przyjazny dla deweloperów |
| Główne zastosowanie | Eksport/import danych, raporty, ETL | API, pliki konfiguracyjne, magazyny NoSQL |
| Rozmiar pliku | Mniejszy (brak powtarzanych nazw kluczy) | Większy (nazwy kluczy powtarzają się dla każdego rekordu) |
| Schemat | Domyślny (wiersz nagłówkowy) | Jawny (lub poprzez JSON Schema) |
Reguła kciuka: wybierz CSV, gdy dane są tabelaryczne, a odbiorcą jest arkusz kalkulacyjny lub potok danych. Wybierz JSON, gdy dane mają hierarchię lub gdy odbiorcą jest API. Wynikowy JSON warto zweryfikować w narzędziu Formatowanie JSON, aby wcześnie wychwycić problemy strukturalne.
Jeśli w projekcie używasz luźniejszych formatów typu JSON5 lub JSONC do konfiguracji, zajrzyj do naszego przewodnika po JSON5 i JSONC, gdzie omawiamy różnice składniowe i narzędzia.
4 sposoby konwersji CSV na JSON
Metoda 1 — narzędzie w przeglądarce
Do jednorazowych konwersji najszybsza ścieżka prowadzi przez przeglądarkę. Wklej CSV do konwertera online, odbierz JSON, a następnie zweryfikuj wynik w narzędziu Formatowanie JSON, aby potwierdzić poprawność struktury.
Przewaga jest jasna: dane nigdy nie opuszczają przeglądarki. Żadnych uploadów, żadnego przetwarzania po stronie serwera, żadnych obaw o prywatność. Ma to znaczenie przy danych wewnętrznych, kluczach API osadzonych w eksportach lub czymkolwiek, czego nie chcesz wysyłać na zewnętrzny serwer.
Najlepsze do: małych plików (poniżej 10 MB), szybkich konwersji ad hoc oraz członków zespołu nietechnicznych.
Metoda 2 — JavaScript / Node.js
Przeglądarka (czysty JS):
function csvToJson(csv) {
const lines = csv.trim().split('\n');
const headers = lines[0].split(',').map(h => h.trim());
return lines.slice(1).map(line => {
const values = line.split(',');
return headers.reduce((obj, header, i) => {
obj[header] = values[i]?.trim() ?? '';
return obj;
}, {});
});
}
const csv = `name,age,city
Alice,30,New York
Bob,25,London`;
console.log(JSON.stringify(csvToJson(csv), null, 2));
To rozwiązanie sprawdzi się dla prostego CSV bez pól w cudzysłowach. Do zastosowań produkcyjnych — z przecinkami w wartościach, znakami nowej linii w polach lub łańcuchami w cudzysłowach — należy użyć właściwego parsera.
Node.js (csv-parser + strumienie):
import { createReadStream } from 'fs';
import { parse } from 'csv-parse';
const records = [];
createReadStream('data.csv')
.pipe(parse({ columns: true, trim: true, skip_empty_lines: true }))
.on('data', (row) => records.push(row))
.on('end', () => {
console.log(JSON.stringify(records, null, 2));
});
Opcja columns: true traktuje pierwszy wiersz jako klucze. Opcja trim usuwa białe znaki z wartości. Takie podejście poprawnie obsługuje pola w cudzysłowach, ucieczki przecinków oraz wartości wielowierszowe.
Metoda 3 — Python
Biblioteka standardowa (zero zależności):
import csv
import json
with open('data.csv', encoding='utf-8') as f:
reader = csv.DictReader(f)
rows = list(reader)
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(rows, f, indent=2, ensure_ascii=False)
csv.DictReader zamienia każdy wiersz w słownik, używając wiersza nagłówkowego jako kluczy. Flaga ensure_ascii=False zachowuje znaki Unicode (chińskie, japońskie, znaki diakrytyczne) zamiast escapować je do postaci \uXXXX.
Pandas (jednolinijkowiec dla data scientistów):
import pandas as pd
df = pd.read_csv('data.csv')
df.to_json('data.json', orient='records', indent=2, force_ascii=False)
Kiedy co wybrać:
- csv + json: lekkie skrypty, funkcje Lambda, kontenery — wszędzie tam, gdzie zależy Ci na minimalnych zależnościach.
- pandas: gdy oprócz konwersji chcesz oczyścić, przefiltrować lub przekształcić dane. Narzut importu pandas zwraca się z nawiązką, jeśli robisz coś więcej niż samą konwersję formatu.
Metoda 4 — narzędzia CLI
Do skryptów powłoki i potoków automatyzacji:
csvkit:
# Install: pip install csvkit
csvjson data.csv > data.json
Miller (mlr):
# Install: brew install miller (macOS) lub apt install miller (Ubuntu)
mlr --csv --json cat data.csv > data.json
Potok z jq do filtrowania:
# Convert and filter in one pipeline
csvjson data.csv | jq '[.[] | select(.age | tonumber > 25)]'
Miller jest szczególnie potężny, ponieważ natywnie obsługuje CSV, JSON, TSV i inne formaty. Dane można przekształcać w trakcie konwersji:
# Convert CSV to JSON, rename a field, add a computed field
mlr --csv --json rename name,fullName then put '$age_group = ($age > 30) ? "senior" : "junior"' data.csv
JSON na CSV: kierunek odwrotny
Konwersja JSON na CSV niesie ze sobą wyzwania, które nie istnieją w kierunku „w przód”.
Spłaszczanie zagnieżdżonych obiektów
CSV jest z natury płaski. Gdy JSON zawiera zagnieżdżone obiekty, potrzebujesz strategii spłaszczania:
{
"name": "Alice",
"address": {
"city": "New York",
"zip": "10001"
}
}
Staje się:
| name | address.city | address.zip |
|---|---|---|
| Alice | New York | 10001 |
Konwencja notacji kropkowej (address.city) to najczęstsze rozwiązanie. W Pythonie:
import pandas as pd
data = [
{"name": "Alice", "address": {"city": "New York", "zip": "10001"}},
{"name": "Bob", "address": {"city": "London", "zip": "EC1A"}}
]
df = pd.json_normalize(data)
df.to_csv('output.csv', index=False)
# Columns: name, address.city, address.zip
Obsługa tablic
Pola tablicowe wymagają decyzji:
| Strategia | Przykład wejścia | Wynik CSV | Najlepsze do |
|---|---|---|---|
| Złączenie w łańcuch | ["admin","editor"] | admin;editor | Proste listy, możliwość ponownego importu |
| Rozwinięcie do kolumn | ["admin","editor"] | role_0: admin, role_1: editor | Tablice o stałej długości |
| Rozwinięcie do wierszy | ["admin","editor"] | Dwa wiersze, po jednym na rolę | Analiza relacyjna |
Wybierz odpowiednio do dalszego odbiorcy. Jeśli CSV trafia z powrotem do bazy danych, najczęściej najlepiej sprawdza się rozwinięcie do wierszy.
Utrata informacji o typach
CSV nie ma żadnego systemu typów. Po konwersji JSON na CSV:
truestaje się łańcuchem"true"— boolean czy łańcuch?nullstaje się pustą komórką — nieodróżnialną od pustego łańcucha"".42staje się"42"— liczba czy łańcuch?
Jeśli wierność rundy (CSV → przetwarzanie → JSON) jest istotna, udokumentuj konwencje typów w komentarzu nagłówkowym lub w towarzyszącym pliku schematu.
5 typowych pułapek i jak ich uniknąć
Te problemy po cichu psują dane. Większość samouczków je pomija. Nie ucz się ich na produkcji.
1. Miny w kodowaniu
Problem: otwierasz CSV od kolegi i widzisz é zamiast é lub 锟斤拷 zamiast chińskich znaków.
Dlaczego się dzieje: plik został zapisany w jednym kodowaniu (Windows-1252, GBK, Shift_JIS), ale parser zakłada UTF-8. Excel pod Windows często zapisuje CSV jako Windows-1252 albo dodaje znacznik UTF-8 BOM (Byte Order Mark — niewidzialne \xEF\xBB\xBF na początku pliku).
Rozwiązanie:
# Detect encoding first
import chardet
with open('data.csv', 'rb') as f:
result = chardet.detect(f.read(10000))
print(result) # {'encoding': 'Windows-1252', 'confidence': 0.73}
# Then read with the correct encoding
with open('data.csv', encoding=result['encoding']) as f:
reader = csv.DictReader(f)
# ...
W Node.js usuń BOM jawnie:
import { readFileSync } from 'fs';
let content = readFileSync('data.csv', 'utf-8');
// Strip UTF-8 BOM if present
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
2. Pomyłka separatorów
Problem: parser produkuje jedną gigantyczną kolumnę zamiast wielu pól.
Dlaczego się dzieje: w wielu lokalizacjach europejskich (Francja, Niemcy, Hiszpania) Excel używa średnika (;) jako separatora pól w CSV, ponieważ przecinek pełni funkcję separatora dziesiętnego (np. 3,14 zamiast 3.14). Pliki rozdzielone tabulatorem (.tsv) to kolejny wariant.
Rozwiązanie: automatycznie wykrywaj separator, próbkując kilka pierwszych wierszy:
import csv
with open('data.csv') as f:
sample = f.read(8192)
dialect = csv.Sniffer().sniff(sample, delimiters=',;\t|')
f.seek(0)
reader = csv.DictReader(f, dialect=dialect)
3. Znikające zera wiodące
Problem: kod pocztowy 00501 staje się 501. Kod produktu 007 zmienia się w 7.
Dlaczego się dzieje: parser (lub Excel) interpretuje pole jako liczbę i obcina zera wiodące. Jest to szczególnie groźne dla kodów pocztowych, numerów telefonu i identyfikatorów.
Rozwiązanie: wymuś typ łańcuchowy. W pandas:
df = pd.read_csv('data.csv', dtype={'zip': str, 'product_code': str})
W JavaScripcie sprawdź, czy oryginalny łańcuch różni się od wyniku parsowania liczbowego:
function preserveLeadingZeros(value) {
if (/^0\d+$/.test(value)) return value; // Keep as string
const num = Number(value);
return isNaN(num) ? value : num;
}
4. Utrata precyzji dużych liczb
Problem: identyfikator 9007199254740993 staje się 9007199254740992 w wynikowym JSON.
Dlaczego się dzieje: typ Number w JavaScripcie to 64-bitowy float (IEEE 754). Liczby całkowite większe niż Number.MAX_SAFE_INTEGER (2^53 − 1 = 9007199254740991) tracą precyzję. Dotyczy to identyfikatorów baz danych, identyfikatorów Snowflake oraz identyfikatorów postów Twittera/X.
Rozwiązanie: trzymaj duże liczby w JSON jako łańcuchy lub używaj BigInt w kodzie przetwarzającym:
// Parse with string preservation for large numbers
function safeParseNumber(value) {
const num = Number(value);
if (Number.isInteger(num) && !Number.isSafeInteger(num)) {
return value; // Keep as string to preserve precision
}
return isNaN(num) ? value : num;
}
5. Niejednoznaczność wartości pustych
Problem: plik CSV zawiera puste komórki. Po konwersji nie wiadomo, czy oryginalna wartość była pustym łańcuchem "", null, czy po prostu brakowało jej w danych.
Dlaczego się dzieje: CSV nie ma sposobu rozróżnienia tych trzech stanów. Puste pole pomiędzy dwoma przecinkami (Alice,,30) może oznaczać dowolny z nich.
Rozwiązanie: zdefiniuj konwencję i stosuj ją konsekwentnie:
def parse_value(value):
if value == '':
return None # or '' — pick one convention
if value == 'NULL' or value == 'null':
return None
return value
Jeśli w danych występują wartości specjalne typu NULL, N/A lub -, udokumentuj je i obsłuż jawnie.
Strumieniowanie dużych plików
Gdy CSV przekracza 100 MB, ładowanie całości do pamięci przestaje wchodzić w grę. Skorzystaj ze strumieniowania.
Node.js (potok strumieni):
import { createReadStream, createWriteStream } from 'fs';
import { parse } from 'csv-parse';
import { Transform } from 'stream';
import { pipeline } from 'stream/promises';
let first = true;
const toJsonArray = new Transform({
objectMode: true,
transform(record, encoding, callback) {
const prefix = first ? '[\n' : ',\n';
first = false;
callback(null, prefix + JSON.stringify(record));
},
flush(callback) {
callback(null, '\n]');
}
});
await pipeline(
createReadStream('large.csv'),
parse({ columns: true, trim: true }),
toJsonArray,
createWriteStream('large.json')
);
Python (generator):
import csv
import json
def csv_rows(path):
with open(path, encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
yield row
# Stream to JSON Lines format (one JSON object per line)
with open('large.jsonl', 'w', encoding='utf-8') as out:
for row in csv_rows('large.csv'):
out.write(json.dumps(row, ensure_ascii=False) + '\n')
W przypadku bardzo dużych plików rozważ format JSON Lines (.jsonl) zamiast pojedynczej tablicy JSON. Każda linia jest niezależnym obiektem JSON, dzięki czemu wynikowy plik możesz przetwarzać linia po linii — bez konieczności parsowania całości naraz.
FAQ
Jaka jest różnica między CSV a JSON?
CSV (Comma-Separated Values) przechowuje dane w postaci płaskiej tabeli, w której każda wartość jest łańcuchem znaków. JSON (JavaScript Object Notation) zapisuje dane strukturalne z zagnieżdżonymi obiektami, tablicami oraz wartościami z typami (łańcuchami, liczbami, wartościami logicznymi, null). CSV jest mniejszy i przyjazny arkuszom kalkulacyjnym; JSON jest bardziej wyrazisty i przyjazny API.
Jak przekonwertować CSV na JSON w JavaScripcie?
W Node.js użyj pakietu csv-parse: odczytaj plik przez createReadStream, przepuść przez parse({ columns: true }) i zbierz wyniki. W przeglądarce odczytaj plik za pomocą FileReader, podziel po znakach nowej linii i przyporządkuj wiersze do obiektów, wykorzystując wiersz nagłówkowy jako klucze.
Jak przekonwertować CSV na JSON w Pythonie?
Użyj csv.DictReader z biblioteki standardowej, aby odczytać wiersze jako słowniki, a następnie json.dump(), aby zapisać je jako tablicę JSON. Do manipulacji danymi przed konwersją alternatywą jest jednolinijkowiec: pandas.read_csv() plus df.to_json(orient='records').
Czy zagnieżdżony JSON da się przekonwertować na CSV?
Tak, ale potrzebujesz strategii spłaszczania. Najpopularniejsze podejście używa notacji kropkowej: pole takie jak address.city staje się nagłówkiem kolumny. W Pythonie obsługuje to automatycznie pandas.json_normalize(). Tablice wymagają dodatkowych decyzji — złączyć je w łańcuchy, rozwinąć do kolumn lub rozwinąć do wierszy.
Dlaczego po konwersji w moim CSV są krzaczki?
Niezgodność kodowania. Plik najprawdopodobniej został zapisany w Windows-1252 lub GBK, podczas gdy parser zakłada UTF-8. Użyj biblioteki wykrywającej kodowanie, na przykład chardet (Python), aby zidentyfikować kodowanie, a następnie podaj je jawnie przy odczycie. Sprawdź też, czy plik nie zaczyna się od UTF-8 BOM, które niektóre narzędzia dodają automatycznie.
Jak obsłużyć plik CSV większy niż 100 MB?
Użyj strumieniowania zamiast ładować cały plik do pamięci. W Node.js przepuść dane przez csv-parse ze strumieniami. W Pythonie iteruj przez csv.DictReader z generatorem. Rozważ wynik w formacie JSON Lines (.jsonl) zamiast pojedynczej tablicy JSON — ułatwia to dalsze przetwarzanie.
Jak zweryfikować, że wynikowy JSON jest poprawny?
Wklej wynik do narzędzia online Formatowanie JSON, aby sprawdzić składnię, strukturę i zagnieżdżenie. Do automatycznej walidacji użyj JSON.parse() w JavaScripcie lub json.loads() w Pythonie — oba zgłaszają czytelne błędy dla nieprawidłowego wejścia. Do walidacji schematu zdefiniuj JSON Schema i waliduj programowo.
Najważniejsze wnioski
- Wybierz odpowiednią metodę do kontekstu: narzędzia w przeglądarce do szybkich przypadków ad hoc, kod do automatyzacji, CLI do potoków.
- Uważaj na problemy z kodowaniem — zawsze podawaj kodowanie jawnie, zamiast polegać na wartościach domyślnych.
- Zachowuj typy świadomie — zera wiodące, duże liczby całkowite oraz wartości null wymagają jawnej obsługi.
- Strumieniuj duże pliki zamiast ładować je do pamięci. Dla bardzo dużych zbiorów rozważ format JSON Lines.
- Waliduj wynik — przepuść skonwertowany JSON przez Formatowanie JSON, aby wychwycić problemy strukturalne, zanim trafią na produkcję.