Jak Formatować JSON w JavaScript — 5 Metod
JSON jest wszędzie: odpowiedzi API, pliki konfiguracyjne, eksporty bazy danych, pipeline'y logów. Ale surowe wyjście JSON jest często zwartą plamą - bez podziałów linii, bez wcięć, klucze w kolejności, w jakiej serializator je wypluje. To czyni debugowanie bolesnym, a współpracę trudniejszą, niż powinna być.
JavaScript daje ci kilka sposobów formatowania JSON, od wbudowanego JSON.stringify po
biblioteki streamingowe dla plików zbyt dużych, aby załadować je do pamięci. Ten przewodnik przepracowuje pięć
praktycznych metod, pokrywając najczęstsze przypadki i przypadki brzegowe, które zaskakują programistów.
Metoda 1: JSON.stringify z Wcięciem
Najprostsze podejście jest już wbudowane w język. JSON.stringify przyjmuje trzy
argumenty: wartość do serializacji, replacer (więcej o tym w Metodzie 2) i parametr space
kontrolujący wcięcie.
const data = {
name: "Alice",
role: "engineer",
skills: ["JavaScript", "TypeScript", "Node.js"],
active: true,
};
// Wcięcie 2-spacjowe (typowa wartość domyślna)
console.log(JSON.stringify(data, null, 2));
// Wcięcie 4-spacjowe
console.log(JSON.stringify(data, null, 4));
// Wcięcie tabulatorem
console.log(JSON.stringify(data, null, "\t"));
Z null, 2 wyjście wygląda tak:
{
"name": "Alice",
"role": "engineer",
"skills": [
"JavaScript",
"TypeScript",
"Node.js"
],
"active": true
}
Dwie spacje to najczęstsza konwencja w projektach JavaScript - pasuje do domyślnego ustawienia w ESLint,
Prettier i większości przewodników stylu. Cztery spacje są częste w narzędziach związanych z Pythonem. Gdy
space jest pominięty lub ustawiony na 0, wyjście jest zwartym stringiem jednoliniowym -
idealnym do transmisji.
Możesz też formatować JSON natychmiast w przeglądarce używając narzędzia JSON Formatter - wklej surowy JSON, dostań pretty-printowane wyjście i skopiuj wynik bez pisania jakiegokolwiek kodu.
Metoda 2: Niestandardowa Funkcja Replacer
Drugi argument JSON.stringify - replacer - to funkcja lub tablica
filtrująca i transformująca wartości przed serializacją. To gdzie dostajesz prawdziwą kontrolę nad tym, co
kończy w wyjściu.
Forma tablicowa to najprostsze podejście do whitelistingu kluczy:
const user = {
id: 42,
name: "Bob",
password: "s3cr3t",
email: "bob@example.com",
createdAt: new Date(),
};
// Replacer tablicowy: uwzględnij tylko wymienione klucze
const safeJson = JSON.stringify(user, ["id", "name", "email"], 2);
console.log(safeJson);
// {
// "id": 42,
// "name": "Bob",
// "email": "bob@example.com"
// }
Forma funkcji daje ci kontrolę per-wartość. Zwróć wartość, aby ją uwzględnić, zwróć
undefined, aby ją pominąć, lub zwróć przekształconą wartość:
function replacer(key, value) {
// Pomiń klucze zaczynające się od podkreślnika (konwencja prywatna)
if (key.startsWith("_")) return undefined;
// Maskuj wrażliwe pola
if (key === "password" || key === "token") return "[REDACTED]";
// Konwertuj obiekty Date na stringi ISO jawnie
if (value instanceof Date) return value.toISOString();
return value;
}
const payload = {
id: 1,
name: "Carol",
password: "hunter2",
_internalFlag: true,
lastLogin: new Date("2026-05-01"),
};
console.log(JSON.stringify(payload, replacer, 2));
// {
// "id": 1,
// "name": "Carol",
// "password": "[REDACTED]",
// "lastLogin": "2026-05-01T00:00:00.000Z"
// } Replacer biegnie rekurencyjnie na każdym zagnieżdżonym obiekcie i tablicy. Ten wzorzec jest użyteczny dla pipeline'ów logowania, gdzie chcesz usunąć poświadczenia przed zapisem na dysk lub wysłaniem do serwisu obserwowalności.
Metoda 3: Pretty-Print z Posortowanymi Kluczami
Obiekty JavaScript nie gwarantują kolejności kluczy (chociaż V8 i większość silników zachowuje kolejność wstawiania dla kluczy stringowych). Gdy potrzebujesz deterministycznego wyjścia - do diffowania, cache'owania lub kanonicznych reprezentacji - sortowanie kluczy alfabetycznie to właściwy ruch.
function sortedStringify(value, indent = 2) {
return JSON.stringify(value, sortReplacer, indent);
}
function sortReplacer(key, value) {
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
return Object.keys(value)
.sort()
.reduce((sorted, k) => {
sorted[k] = value[k];
return sorted;
}, {});
}
return value;
}
const config = {
version: "1.0",
author: "Dave",
dependencies: { typescript: "^5.4", eslint: "^9.0", astro: "^5.0" },
name: "my-project",
};
console.log(sortedStringify(config));
// {
// "author": "Dave",
// "dependencies": { "astro": "^5.0", "eslint": "^9.0", "typescript": "^5.4" },
// "name": "my-project",
// "version": "1.0"
// }
Posortowane klucze oznaczają, że diffy git pokazują tylko linie, które faktycznie się zmieniły, zamiast arbitralnych
reorganizacji z różnych serializatorów. To szczególnie użyteczne dla package.json
i podobnych plików konfiguracyjnych zapisanych w kontroli wersji.
Do konwertowania JSON na inne formaty, narzędzie JSON do YAML i narzędzie JSON do CSV również obsługują porządkowanie kluczy w swoim wyjściu.
Metoda 4: Formatowanie ze Stringa (Parse + Stringify)
Rzeczywisty JSON zwykle przychodzi jako string - z odpowiedzi fetch, odczytu pliku, wklejenia ze schowka lub kolumny TEXT bazy danych. Musisz go najpierw sparsować, potem przeformatować. Krytyczny element to właściwa obsługa błędów: niepoprawny JSON rzuci, a chcesz to wyłapać łagodnie.
function formatJsonString(rawString, indent = 2) {
try {
const parsed = JSON.parse(rawString);
return { ok: true, result: JSON.stringify(parsed, null, indent) };
} catch (err) {
return { ok: false, error: err.message };
}
}
const raw = '{"name":"Eve","scores":[100,95,88],"active":true}';
const { ok, result, error } = formatJsonString(raw);
if (ok) {
console.log(result);
// {
// "name": "Eve",
// "scores": [100, 95, 88],
// "active": true
// }
} else {
console.error("Parsowanie zawiodło:", error);
}
// Niepoprawne wejście
const bad = '{"name": "Eve", "broken":}';
const r2 = formatJsonString(bad);
// { ok: false, error: "Unexpected token '}'" } Opakowanie błędów parsowania w ustrukturyzowany obiekt zwrotny czyni tę funkcję bezpieczną do użycia w komponentach UI i skryptach buildowych bez otaczania każdego miejsca wywołania try/catchem. Dokumentacja MDN dla JSON.stringify pokrywa pełną specyfikację parametrów, a RFC 8259 definiuje, jak wygląda poprawny JSON na poziomie protokołu.
Metoda 5: Streaming dla Dużych Plików
Metody 1-4 wszystkie ładują całą strukturę JSON do pamięci przed formatowaniem. Dla plików w zakresie setek megabajtów lub wielogigabajtowych, to blokuje pętlę zdarzeń Node.js i może crashnąć proces całkowicie.
Podejście streamingowe czyta plik w kawałkach i pisze sformatowane wyjście przyrostowo. Dla NDJSON
(jeden obiekt JSON na linię, częste w plikach logów i eksportach baz danych), podejście oparte na
readline działa bez dodatkowych zależności:
import { createReadStream, createWriteStream } from "node:fs";
import { createInterface } from "node:readline";
async function formatNdjsonFile(inputPath, outputPath) {
const rl = createInterface({
input: createReadStream(inputPath),
crlfDelay: Infinity,
});
const output = createWriteStream(outputPath);
for await (const line of rl) {
if (!line.trim()) continue;
try {
const obj = JSON.parse(line);
output.write(JSON.stringify(obj, null, 2) + "\n---\n");
} catch (e) {
output.write("[Niepoprawna linia JSON: " + e.message + "]\n---\n");
}
}
output.end();
} NDJSON to najprostszy format streamingowy: każda linia to poprawny, kompletny obiekt JSON. Wiele narzędzi eksportu wspiera go właśnie dlatego, że jest trywialnie streamowalny. Jeśli kontrolujesz format dużych eksportów danych, preferuj NDJSON nad jedną gigantyczną tablicą JSON.
Przypadki Brzegowe na Które Trzeba Uważać
To scenariusze, gdzie standardowe formatowanie JSON albo zawodzi po cichu, albo rzuca nieoczekiwanie.
Referencje Cykliczne
JSON.stringify rzuca TypeError, jeśli obiekt odnosi się do siebie bezpośrednio
lub pośrednio. Napraw to replacerem śledzącym odwiedzone obiekty używając WeakSet:
function safeStringify(obj, indent = 2) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circular]";
seen.add(value);
}
return value;
}, indent);
}
const a = { name: "circular" };
a.self = a;
console.log(safeStringify(a));
// { "name": "circular", "self": "[Circular]" } WeakSet trzyma referencje bez blokowania garbage collection, co unika wycieków pamięci
w długo działających procesach.
Wartości BigInt
JSON.stringify rzuca TypeError dla wartości BigInt, ponieważ
specyfikacja JSON nie ma typu liczb całkowitych 64-bitowych. Konwertuj na string w swoim replacerze:
const data = { id: 9007199254740993n, value: 42 };
JSON.stringify(data, (key, value) =>
typeof value === "bigint" ? value.toString() : value
, 2);
// { "id": "9007199254740993", "value": 42 } Wartości Map i Set
Map serializuje się jako pusty obiekt, a Set serializuje się jako pusta tablica -
nie ich zawartość. Konwertuj je jawnie w replacerze:
const data = {
tags: new Set(["json", "javascript"]),
meta: new Map([["source", "api"]]),
};
JSON.stringify(data, (key, value) => {
if (value instanceof Set) return [...value];
if (value instanceof Map) return Object.fromEntries(value);
return value;
}, 2);
// { "tags": ["json", "javascript"], "meta": { "source": "api" } } Wartości undefined
Własności obiektu z wartościami undefined są po cichu pomijane. Sloty tablic z
undefined stają się null. Użyj replacera, aby konwertować undefined
na null, gdy potrzebujesz zachować wszystkie klucze:
const obj = { a: 1, b: undefined, c: null };
JSON.stringify(obj, null, 2);
// { "a": 1, "c": null } - "b" po cichu pominięte
// Naprawa:
JSON.stringify(obj, (key, value) =>
value === undefined ? null : value
, 2);
// { "a": 1, "b": null, "c": null } Formatuj JSON Natychmiast w Przeglądarce
Jeśli musisz sformatować plamę JSON teraz bez pisania żadnego kodu, Toova JSON Formatter obsługuje to jednym kliknięciem - wklej surowy JSON, dostań pretty-printowane wyjście z wcięciem 2- lub 4-spacjowym i skopiuj wynik. Bez rejestracji, bez uploadu plików, wszystko działa lokalnie w twojej przeglądarce.
Do konwersji między formatami, JSON do YAML i JSON do CSV podążają tym samym podejściem privacy-first - twoje dane nigdy nie opuszczają twojego urządzenia.
Wnioski
Dla większości przypadków użycia, JSON.stringify(obj, null, 2) to wszystko, czego potrzebujesz. Dodaj funkcję replacer,
gdy potrzebujesz filtrowania, maskowania lub posortowanych kluczy. Opakuj JSON.parse w
try/catch przy obsłudze zewnętrznego wejścia. Sięgaj po streaming tylko wtedy, gdy rozmiar pliku czyni synchroniczne
parsowanie niepraktycznym. I trzymaj w głowie przypadki brzegowe - referencje cykliczne, BigInt,
Map/Set, undefined - podczas pracy
z nietypowymi kształtami danych.