JSON Formatter JavaScript — JSON.stringify()
Użyj darmowego Formater i Upiększacz JSON bezpośrednio w przeglądarce — bez instalacji.
Wypróbuj Formater i Upiększacz JSON online →Kiedy debuguję odpowiedzi API w Node.js, ściana zminifikowanego JSON jest pierwszą rzeczą, która mnie spowalnia — jedno wywołanie JSON.stringify(data, null, 2) i struktura staje się natychmiast czytelna. Do formatowania JSON w JavaScript nie potrzebujesz niczego poza środowiskiem uruchomieniowym: JSON.stringify jest wbudowane w każdą przeglądarkę i środowisko Node.js, bez instalacji. Jeśli potrzebujesz tylko szybkiego wyniku bez pisania kodu, Formatter JSON od ToolDeck zrobi to natychmiast. Ten przewodnik omawia wszystko, co praktyczne: parametr space, tablice i funkcje replacera, obsługę Date, BigInt i odwołań cyklicznych, odczyt i zapis plików JSON w Node.js (18+), czytelne wyświetlanie z CLI oraz bibliotekę fast-json-stringify do serializacji produkcyjnej.
- ✓JSON.stringify(data, null, 2) jest wbudowane w każdą przeglądarkę i Node.js — bez instalacji.
- ✓Parametr replacer przyjmuje tablicę (lista dozwolonych kluczy) lub funkcję (transformacja wartości) — użyj go do maskowania wrażliwych pól.
- ✓Obiekty Date są automatycznie serializowane przez toJSON() → ciąg ISO 8601; BigInt wyrzuca TypeError i wymaga własnego replacera.
- ✓Odwołania cykliczne wyrzucają TypeError — napraw je za pomocą seen Set w funkcji replacera lub użyj biblioteki flatted.
- ✓Do czytelnego wyświetlania z CLI użyj node -p "JSON.stringify(require('./file.json'),null,2)" — bez dodatkowych narzędzi.
Czym jest formatowanie JSON?
Formatowanie JSON (zwane też pretty-printing) przekształca zwarty, zminifikowany ciąg JSON w czytelny dla człowieka układ ze spójnymi wcięciami i podziałami wierszy. Dane bazowe są identyczne — zmienia się tylko białe znaki. Zwarty JSON jest optymalny do transferu sieciowego, gdzie każdy bajt ma znaczenie; sformatowany JSON jest optymalny do debugowania, przeglądu kodu i analizy logów. Funkcja JSON.stringify() w JavaScript obsługuje obie możliwości w jednym wywołaniu przez przełączenie parametru space.
{"orderId":"ord_8f2a91bc","status":"shipped","items":[{"sku":"HDMI-4K-2M","qty":2,"unitPrice":12.99}],"total":25.98}{
"orderId": "ord_8f2a91bc",
"status": "shipped",
"items": [
{
"sku": "HDMI-4K-2M",
"qty": 2,
"unitPrice": 12.99
}
],
"total": 25.98
}JSON.stringify() — Wbudowany formatter JSON
JSON.stringify() to globalna funkcja w każdym środowisku JavaScript — przeglądarki, Node.js, Deno, Bun — bez potrzeby importowania. Trzeci argument, space, steruje wcięciem: przekaż liczbę, aby uzyskać tyle spacji na każdy poziom, lub ciąg '\t' dla tabulatorów. Pominięcie (lub przekazanie null) daje zwarte wyjście w jednej linii.
const serverConfig = {
host: "api.payments.internal",
port: 8443,
workers: 4,
tls: { enabled: true, cert: "/etc/ssl/certs/api.pem" },
rateLimit: { requestsPerMinute: 1000, burst: 50 }
}
console.log(JSON.stringify(serverConfig, null, 2))
// {
// "host": "api.payments.internal",
// "port": 8443,
// "workers": 4,
// "tls": {
// "enabled": true,
// "cert": "/etc/ssl/certs/api.pem"
// },
// "rateLimit": {
// "requestsPerMinute": 1000,
// "burst": 50
// }
// }Parametr space przyjmuje liczbę (1–10 spacji) lub ciąg znaków. Przekazanie znaku tabulacji daje wyjście, które wiele edytorów i narzędzi diff preferuje. Oto praktyczny wzorzec, którego używam przy zapisywaniu sformatowanego JSON do plików konfiguracyjnych:
const telemetryEvent = {
eventId: "evt_3c7f9a2b",
service: "checkout-api",
severity: "warn",
latencyMs: 342,
region: "eu-central-1",
tags: ["payment", "timeout", "retry"]
}
// wcięcie 2 spacje (najczęstsze w projektach JS)
JSON.stringify(telemetryEvent, null, 2)
// wcięcie tabulatorem (preferowane przez niektóre lintery i narzędzia konfiguracyjne)
JSON.stringify(telemetryEvent, null, '\t')
// zwarty — bez białych znaków (do transferu sieciowego)
JSON.stringify(telemetryEvent)
// {"eventId":"evt_3c7f9a2b","service":"checkout-api",...}undefined, funkcje i Symbol są po cichu pomijane z wyjścia. Jeśli wartość właściwości to undefined, klucz nie pojawi się w zserializowanym ciągu — to częste źródło błędów przy logowaniu obiektów z opcjonalnymi polami.Funkcje replacera — filtrowanie i transformacja wyjścia
Drugi argument JSON.stringify() to replacer. Istnieje w dwóch formach: jako tablica umieszczająca określone klucze na liście dozwolonych, lub jako funkcja wywoływana dla każdej pary klucz/wartość, która może filtrować, transformować lub redagować wartości. Po formę tablicową sięgam, gdy potrzebuję szybkiego podzbioru, a po formę funkcyjną, gdy muszę zamaskować wrażliwe dane przed logowaniem.
Tablica jako replacer — lista dozwolonych kluczy
const order = {
orderId: "ord_8f2a91bc",
customer: {
id: "usr_4421",
email: "p.kowalski@przyklad.pl",
passwordHash: "bcrypt:$2b$12$XKzV..."
},
items: [{ sku: "HDMI-4K-2M", qty: 2, unitPrice: 12.99 }],
createdAt: "2026-03-10T14:22:00Z"
}
// Dołącz tylko bezpieczne pola do wyjścia logu
const safeLog = JSON.stringify(order, ["orderId", "items", "createdAt"], 2)
// {
// "orderId": "ord_8f2a91bc",
// "items": [{ "sku": "HDMI-4K-2M", "qty": 2, "unitPrice": 12.99 }],
// "createdAt": "2026-03-10T14:22:00Z"
// }
// passwordHash i customer.email zostały wykluczoneFunkcja jako replacer — transformacja wartości
const auditRecord = {
requestId: "req_7d2e91",
user: { id: "usr_4421", email: "p.kowalski@przyklad.pl", apiKey: "sk-live-eKx9..." },
action: "update_billing",
timestamp: new Date("2026-03-10T14:22:00Z"),
durationMs: 87
}
function safeReplacer(key, value) {
// Redaguj pola wyglądające jak sekrety lub dane osobowe
if (key === "apiKey") return "[REDACTED]"
if (key === "email") return value.replace(/(?<=.{2}).+(?=@)/, "***")
return value
}
console.log(JSON.stringify(auditRecord, safeReplacer, 2))
// {
// "requestId": "req_7d2e91",
// "user": { "id": "usr_4421", "email": "p.***@przyklad.pl", "apiKey": "[REDACTED]" },
// "action": "update_billing",
// "timestamp": "2026-03-10T14:22:00.000Z",
// "durationMs": 87
// }this ustawionym na obiekt zawierający bieżący klucz. Pierwsze wywołanie przekazuje pusty ciąg jako klucz i całą serializowaną wartość jako wartość — zwróć ją bez zmian, aby kontynuować normalną serializację.Obsługa typów nieserializowalnych
Nie wszystkie wartości JavaScript mapują się czysto na JSON. Znajomość zachowania każdego typu zapobiega cichej utracie danych i nieoczekiwanym błędom środowiska uruchomieniowego w kodzie produkcyjnym.
Date — automatycznie przez toJSON()
Obiekty Date implementują metodę toJSON(), która zwraca ciąg ISO 8601. JSON.stringify() wywołuje toJSON() automatycznie przed serializacją, więc nie jest potrzebna własna obsługa.
const webhook = {
eventType: "payment.succeeded",
occurredAt: new Date("2026-03-10T14:22:00Z"),
processedAt: new Date()
}
JSON.stringify(webhook, null, 2)
// {
// "eventType": "payment.succeeded",
// "occurredAt": "2026-03-10T14:22:00.000Z",
// "processedAt": "2026-03-10T14:22:01.347Z"
// }
// Każdy obiekt z metodą toJSON() jest traktowany tak samo:
const custom = { toJSON: () => "custom-value", hidden: 42 }
JSON.stringify(custom) // '"custom-value"'Własne klasy — implementacja toJSON()
Każda klasa może implementować metodę toJSON(), a JSON.stringify() wywoła ją automatycznie podczas serializacji. Jest to czystsze niż globalny replacer dla typów domenowych, które pojawiają się w całej bazie kodu.
class Money {
constructor(amount, currency) {
this.amount = amount
this.currency = currency
}
toJSON() {
// Wywoływana automatycznie przez JSON.stringify
return { amount: this.amount, currency: this.currency, formatted: `${this.currency} ${this.amount.toFixed(2)}` }
}
}
class OrderId {
constructor(id) { this.id = id }
toJSON() { return this.id } // Serializuj jako zwykły ciąg
}
const invoice = {
invoiceId: new OrderId('inv_8f2a91bc'),
subtotal: new Money(199.00, 'PLN'),
tax: new Money(15.92, 'PLN'),
issuedAt: new Date('2026-03-10T14:22:00Z')
}
JSON.stringify(invoice, null, 2)
// {
// "invoiceId": "inv_8f2a91bc",
// "subtotal": { "amount": 199, "currency": "PLN", "formatted": "PLN 199.00" },
// "tax": { "amount": 15.92, "currency": "PLN", "formatted": "PLN 15.92" },
// "issuedAt": "2026-03-10T14:22:00.000Z"
// }toJSON() ma priorytet nad funkcją replacera. Jeśli obie są obecne, toJSON() działa jako pierwsze — replacer otrzymuje już przekonwertowaną wartość, nie oryginalną instancję klasy.BigInt — TypeError bez replacera
// To wyrzuca: TypeError: Do not know how to serialize a BigInt
// JSON.stringify({ sessionId: 9007199254741234n })
// Rozwiązanie: przekonwertuj BigInt na ciąg w replacerze
function bigIntReplacer(_key, value) {
return typeof value === 'bigint' ? value.toString() : value
}
const metrics = {
requestCount: 9007199254741234n, // przekracza Number.MAX_SAFE_INTEGER
service: "ingestion-worker",
region: "eu-central-1"
}
JSON.stringify(metrics, bigIntReplacer, 2)
// {
// "requestCount": "9007199254741234",
// "service": "ingestion-worker",
// "region": "eu-central-1"
// }Odwołania cykliczne — TypeError bez seen Set
// To wyrzuca: TypeError: Converting circular structure to JSON
// const node = { id: "n1" }; node.self = node; JSON.stringify(node)
// Rozwiązanie: śledzenie widzianych obiektów za pomocą WeakSet
function circularReplacer() {
const seen = new WeakSet()
return function (_key, value) {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]'
seen.add(value)
}
return value
}
}
const parent = { id: "node_parent", label: "root" }
const child = { id: "node_child", parent }
parent.child = child // cykliczne
JSON.stringify(parent, circularReplacer(), 2)
// {
// "id": "node_parent",
// "label": "root",
// "child": { "id": "node_child", "parent": "[Circular]" }
// }
// Alternatywnie: npm install flatted
// import { stringify } from 'flatted'
// stringify(parent) // obsługuje odwołania cykliczne natywnieDokumentacja parametrów JSON.stringify()
Wszystkie trzy parametry są w pełni obsługiwane w każdym nowoczesnym środowisku uruchomieniowym JavaScript. Wartości domyślne dają zwarty JSON w jednej linii — podaj parametry jawnie, aby uzyskać czytelne wyjście.
Formatowanie JSON z pliku i odpowiedzi API
W Node.js (18+) często trzeba przeformatować plik konfiguracyjny JSON lub czytelnie wydrukować odpowiedź API podczas debugowania. Oba wzorce stosują to samo dwuetapowe podejście: sparsuj surowy tekst za pomocą JSON.parse(), a następnie ponownie zserializuj przez JSON.stringify().
Odczyt i nadpisanie pliku konfiguracyjnego JSON
import { readFileSync, writeFileSync } from 'fs'
try {
const raw = readFileSync('./config/database.json', 'utf8')
const config = JSON.parse(raw)
writeFileSync('./config/database.json', JSON.stringify(config, null, 2))
console.log('Konfiguracja przeformatowana pomyślnie')
} catch (err) {
console.error('Przeformatowanie konfiguracji nie powiodło się:', err.message)
// JSON.parse wyrzuca SyntaxError, jeśli plik zawiera nieprawidłowy JSON
// readFileSync wyrzuca ENOENT, jeśli plik nie istnieje
}Asynchroniczne przeformatowanie pliku (fs/promises)
import { readFile, writeFile } from 'fs/promises'
async function reformatJson(filePath) {
const raw = await readFile(filePath, 'utf8')
const parsed = JSON.parse(raw)
const formatted = JSON.stringify(parsed, null, 2)
await writeFile(filePath, formatted, 'utf8')
return { keys: Object.keys(parsed).length }
}
// Użycie
const { keys } = await reformatJson('./config/feature-flags.json')
console.log(`Przeformatowano ${keys} kluczy najwyższego poziomu`)Czytelne wydrukowanie JSON z odpowiedzi fetch()
Podczas budowania lub debugowania klienta API w Node.js 18+ lub przeglądarce, czytelne wydrukowanie treści odpowiedzi to najszybszy sposób zrozumienia, co zwrócił serwer. Standardowy wzorzec to przekazanie response.json() (sparsowany obiekt) do JSON.stringify(). Jeśli potrzebujesz najpierw surowego ciągu — np. do obliczenia hasha lub dosłownego zalogowania — użyj response.text(), a następnie JSON.parse().
// Wzorzec 1: response.json() → czytelne wydrukowanie
async function debugEndpoint(url) {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }
})
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`)
const data = await res.json()
console.log(JSON.stringify(data, null, 2))
}
await debugEndpoint('https://api.stripe.com/v1/charges?limit=3')// Wzorzec 2: response.text() → parsowanie → formatowanie
// Przydatne gdy chcesz zalogować zarówno surową odpowiedź, jak i czytelne wyjście
async function inspectRawResponse(url) {
const res = await fetch(url)
const raw = await res.text()
console.log('Długość surowej odpowiedzi:', raw.length)
try {
const parsed = JSON.parse(raw)
console.log('Sformatowane:')
console.log(JSON.stringify(parsed, null, 2))
} catch {
console.error('Odpowiedź nie jest prawidłowym JSON:', raw.slice(0, 200))
}
}Czytelne wydrukowanie z linii poleceń
Node.js ma wystarczające możliwości, aby czytelnie wydrukować JSON z terminala bez żadnych dodatkowych narzędzi. Te jednoliniowe polecenia są przydatne w skryptach CI, potokach wdrożeniowych i aliasach powłoki. Dla jeszcze szybszego użycia interaktywnego zainstaluj jq — de facto standard do manipulacji JSON w wierszu poleceń.
# Czytelne wydrukowanie package.json za pomocą node -p (print)
node -p "JSON.stringify(require('./package.json'), null, 2)"
# Czytelne wydrukowanie ze stdin (przyjazne dla potoków, działa w CI)
echo '{"port":3000,"env":"production"}' | node -e "
const d = require('fs').readFileSync(0, 'utf8')
console.log(JSON.stringify(JSON.parse(d), null, 2))
"
# Czytelne wydrukowanie odpowiedzi API zapisanej do pliku
cat api-response.json | node -e "
process.stdin.setEncoding('utf8')
let s = ''
process.stdin.on('data', c => s += c)
process.stdin.on('end', () => console.log(JSON.stringify(JSON.parse(s), null, 2)))
"# Python jako zapasowe rozwiązanie (preinstalowany na macOS i większości dystrybucji Linux)
cat api-response.json | python3 -m json.tool
# jq — najszybszy i najbardziej rozbudowany (brew install jq / apt install jq)
cat api-response.json | jq .
# jq — czytelne wydrukowanie i filtrowanie w jednym kroku
cat api-response.json | jq '.data.users[] | {id, email}'"type": "module" w package.json), require() nie jest dostępne w jednoliniowych poleceniach. Użyj --input-type=module z fs.readFileSync zamiast tego, lub przełącz się na node -e ze snippetem CommonJS jak pokazano powyżej.Jeśli w ogóle nie jesteś w terminalu — wklejasz odpowiedź z Postmana lub plik z logami — Formatter JSON od ToolDeck pozwala wkleić, sformatować i skopiować w jednym kroku z podświetlaniem składni i wbudowaną walidacją.
Wydajna alternatywa — fast-json-stringify
fast-json-stringify generuje dedykowaną funkcję serializatora na podstawie schematu JSON. Ponieważ zna kształt danych z wyprzedzeniem, może pominąć sprawdzanie typów i użyć konkatenacji ciągów zamiast rekurencyjnego przechodzenia — benchmarki zazwyczaj pokazują 2–5× wzrost przepustowości w porównaniu do JSON.stringify() przy dużych, powtarzalnych ładunkach. Używam go w trasach API o wysokiej częstotliwości, gdzie koszt serializacji pojawia się w śledzeniu profilowania.
npm install fast-json-stringify
import fastJson from 'fast-json-stringify'
const serializeTelemetryEvent = fastJson({
title: 'TelemetryEvent',
type: 'object',
properties: {
eventId: { type: 'string' },
service: { type: 'string' },
severity: { type: 'string', enum: ['info', 'warn', 'error'] },
latencyMs: { type: 'integer' },
timestamp: { type: 'string' },
region: { type: 'string' }
},
required: ['eventId', 'service', 'severity', 'latencyMs', 'timestamp']
})
const event = {
eventId: 'evt_3c7f9a2b',
service: 'checkout-api',
severity: 'warn',
latencyMs: 342,
timestamp: new Date().toISOString(),
region: 'eu-central-1'
}
const json = serializeTelemetryEvent(event)
// '{"eventId":"evt_3c7f9a2b","service":"checkout-api","severity":"warn",...}'fast-json-stringify jest zaprojektowany do serializacji produkcyjnej ustrukturyzowanych danych — zawsze generuje zwarte wyjście (bez pretty-printing). Do czytelnego wyjścia podczas programowania używaj normalnie JSON.stringify(data, null, 2).Wyjście terminala z podświetlaniem składni
Wbudowana funkcja util.inspect() w Node.js generuje kolorowe, czytelne dla człowieka wyjście zoptymalizowane pod wyświetlanie w terminalu. Obsługuje natywnie odwołania cykliczne i BigInt, a zagnieżdżone obiekty renderuje rekurencyjnie na dowolną głębokość. Wyjście nie jest prawidłowym JSON — używa składni JavaScript (np. renderuje funkcje i Symbole zamiast je pomijać), co czyni je idealnym do debugowania w Node.js, ale nieodpowiednim dla odpowiedzi API lub wyjścia do pliku.
import { inspect } from 'util'
const payload = {
requestId: "req_7d2e91",
user: { id: "usr_4421", roles: ["admin", "billing"] },
metadata: { ipAddress: "203.0.113.42", userAgent: "Mozilla/5.0" },
createdAt: new Date()
}
// depth: null → rozwiń wszystkie zagnieżdżone poziomy; colors: true → kolory ANSI terminala
console.log(inspect(payload, { colors: true, depth: null }))util.inspect() do JSON zapisywanego do plików, wysyłanego przez sieć lub przechowywanenego w bazie danych. Wyjście nie jest prawidłowym JSON i spowoduje błędy parsowania w każdym systemie downstream wywołującym JSON.parse(). Zarezerwuj je wyłącznie do interaktywnego debugowania w terminalu.Praca z dużymi plikami JSON
JSON.parse() ładuje cały plik do pamięci przed parsowaniem — bez problemu przy małych ładunkach, ale nieodpowiednie dla plików powyżej 50–100 MB, takich jak eksporty bazy danych, zrzuty logów aplikacji lub wsady analityczne. W takich przypadkach Node.js Streams i biblioteka stream-json pozwalają przetwarzać rekordy jeden po drugim bez zapychania sterty.
Strumieniowe parsowanie z stream-json
npm install stream-json
import { pipeline } from 'stream/promises'
import { createReadStream } from 'fs'
import { parser } from 'stream-json'
import { streamArray } from 'stream-json/streamers/StreamArray.js'
// events.json = tablica milionów obiektów — nigdy nie ładowana w całości do pamięci
await pipeline(
createReadStream('./events.json'),
parser(),
streamArray(),
async function* (source) {
for await (const { value: event } of source) {
if (event.severity === 'error') {
console.log(JSON.stringify(event, null, 2))
}
}
}
)NDJSON / JSON Lines — bez dodatkowych zależności
NDJSON (Newline Delimited JSON) przechowuje jeden obiekt JSON na linię i jest powszechny w eksportach Kafka, wynikach BigQuery i potokach logów strukturalnych. Wbudowany moduł readline w Node.js obsługuje go bez żadnych pakietów zewnętrznych.
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
// Format: jeden obiekt JSON na linię (logi, eksporty Kafka, BigQuery)
const rl = createInterface({
input: createReadStream('./logs.ndjson'),
crlfDelay: Infinity
})
for await (const line of rl) {
if (!line.trim()) continue
const entry = JSON.parse(line)
if (entry.level === 'error') {
console.log(JSON.stringify(entry, null, 2))
}
}JSON.parse() na strumieniowanie, gdy plik JSON przekracza 50–100 MB lub gdy przetwarzasz nieograniczony strumień (Kafka, potok logów). Dla NDJSON / JSON Lines używaj readline — bez dodatkowych zależności.Typowe błędy
Te cztery błędy pojawiają się regularnie w przeglądach kodu i raportach o błędach produkcyjnych. Każdy z nich dotyczy subtelnego zachowania JSON.stringify(), które łatwo przeoczyć i trudno debugować po fakcie.
Problem: Właściwości obiektu z wartościami undefined są całkowicie pomijane w wyjściu JSON — bez ostrzeżenia ani błędu. Powoduje to niewidoczną utratę danych, gdy obiekt ma opcjonalne pola.
Rozwiązanie: Używaj null dla celowo nieobecnych wartości, które muszą pojawić się w zserializowanym wyjściu. Zarezerwuj undefined tylko dla pól, które powinny być wykluczone z JSON.
const userProfile = {
userId: "usr_4421",
displayName: "Piotr Kowalski",
avatarUrl: undefined, // zniknie po cichu
bio: undefined // zniknie po cichu
}
JSON.stringify(userProfile, null, 2)
// { "userId": "usr_4421", "displayName": "Piotr Kowalski" }
// avatarUrl i bio zniknęły — bez ostrzeżeniaconst userProfile = {
userId: "usr_4421",
displayName: "Piotr Kowalski",
avatarUrl: null, // jawnie nieobecny — pojawia się w wyjściu
bio: null // jawnie nieobecny — pojawia się w wyjściu
}
JSON.stringify(userProfile, null, 2)
// {
// "userId": "usr_4421",
// "displayName": "Piotr Kowalski",
// "avatarUrl": null,
// "bio": null
// }Problem: Przekazanie wartości BigInt do JSON.stringify() wyrzuca TypeError w czasie wykonania. To jest twarda awaria, a nie ciche pominięcie — pojawi się w produkcji, jeśli jakiekolwiek pole liczbowe przekroczy Number.MAX_SAFE_INTEGER.
Rozwiązanie: Użyj funkcji replacera, która konwertuje wartości BigInt na ciągi przed serializacją. Alternatywnie przekonwertuj BigInt na ciąg lub Number na warstwie danych przed przekazaniem do JSON.stringify.
const session = {
sessionId: 9007199254741234n, // literał BigInt
userId: "usr_4421",
startedAt: "2026-03-10T14:00:00Z"
}
JSON.stringify(session, null, 2)
// Uncaught TypeError: Do not know how to serialize a BigIntfunction bigIntReplacer(_key, value) {
return typeof value === 'bigint' ? value.toString() : value
}
const session = {
sessionId: 9007199254741234n,
userId: "usr_4421",
startedAt: "2026-03-10T14:00:00Z"
}
JSON.stringify(session, bigIntReplacer, 2)
// {
// "sessionId": "9007199254741234",
// "userId": "usr_4421",
// "startedAt": "2026-03-10T14:00:00Z"
// }Problem: Obiekty, które odwołują się do siebie samych — częste w drzewach DOM, listach połączonych i niektórych zestawach wyników ORM — wyrzucają TypeError po przekazaniu do JSON.stringify(). Błąd pojawia się dopiero podczas serializacji, często daleko od miejsca, gdzie odwołanie cykliczne zostało utworzone.
Rozwiązanie: Użyj funkcji replacera z WeakSet do wykrywania i zastępowania odwołań cyklicznych, lub zainstaluj bibliotekę flatted jako zamiennik obsługujący struktury cykliczne natywnie.
const dept = { id: "dept_eng", name: "Inżynieria" }
const team = { id: "team_frontend", dept }
dept.teams = [team] // cykliczne: dept → team → dept
JSON.stringify(dept, null, 2)
// Uncaught TypeError: Converting circular structure to JSONimport { stringify } from 'flatted' // npm install flatted
const dept = { id: "dept_eng", name: "Inżynieria" }
const team = { id: "team_frontend", dept }
dept.teams = [team]
// flatted obsługuje odwołania cykliczne — uwaga: wyjście to format flatted, nie standardowy JSON
stringify(dept)
// Lub użyj replacera opartego na WeakSet, jeśli potrzebujesz standardowego JSON:
function circularReplacer() {
const seen = new WeakSet()
return (_key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]'
seen.add(value)
}
return value
}
}
JSON.stringify(dept, circularReplacer(), 2)Problem: Przekazanie wartości space większej niż 10 do JSON.stringify() nie wyrzuca błędu — wartość jest po cichu obcinana do 10. Programiści oczekujący 20 spacji wcięcia dla głębokiego zagnieżdżenia otrzymają tylko 10, co prowadzi do nieoczekiwanego formatowania w generowanych plikach.
Rozwiązanie: Maksymalne wcięcie to 10 spacji. W przypadku głębokich struktur preferuj wcięcie 2-spacjowe (najczęstsza konwencja w projektach JavaScript) i polegaj na edytorach z możliwością zwijania do nawigacji.
const deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }
// Oczekujemy wcięcia 20 spacji — ale space jest obcinane do 10
JSON.stringify(deepConfig, null, 20)
// To samo wyjście co JSON.stringify(deepConfig, null, 10)
// Brak błędu, brak ostrzeżenia — po cichu obcięteconst deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }
// Używaj 2 (większość projektów) lub 4 spacji — nigdy nie przekraczaj 10
JSON.stringify(deepConfig, null, 2)
// {
// "server": {
// "tls": {
// "certs": {
// "primary": "/etc/ssl/api.pem"
// }
// }
// }
// }JSON.stringify vs alternatywy — szybkie porównanie
Różne sytuacje wymagają różnych narzędzi. JSON.stringify z replacerem pokrywa większość przypadków produkcyjnych bez żadnych zależności. util.inspect to właściwy wybór do szybkiego debugowania w terminalu, gdy potrzebne jest kolorowe wyjście bez konieczności uzyskania prawidłowego JSON. fast-json-stringify opłaca się w trasach o wysokiej przepustowości, gdzie profilowanie wykazuje koszty serializacji; w pozostałych przypadkach narzut związany z utrzymaniem schematu nie jest tego wart.
Często zadawane pytania
Jak czytelnie wyświetlić JSON w JavaScript?
Wywołaj JSON.stringify(data, null, 2) — trzeci argument steruje wcięciem. Przekaż 2 lub 4 dla spacji albo "\t" dla tabulatorów. Nie wymaga żadnego importu ani instalacji: JSON to globalny obiekt dostępny w każdym środowisku JavaScript, zarówno w przeglądarkach, jak i w Node.js.
const config = { host: "api.payments.internal", port: 8443, tls: true }
console.log(JSON.stringify(config, null, 2))
// {
// "host": "api.payments.internal",
// "port": 8443,
// "tls": true
// }Do czego służy parametr space w JSON.stringify()?
Parametr space steruje wcięciem w wyjściu. Przekaż liczbę (1–10), aby uzyskać tyle spacji na każdy poziom, lub ciąg znaków taki jak "\t", aby użyć znaku tabulacji. Wartości powyżej 10 są po cichu obcinane do 10. Przekazanie null, 0 lub pominięcie parametru daje zwarty JSON w jednej linii.
const data = { service: "payments", version: 3, active: true }
JSON.stringify(data, null, 2) // wcięcie 2 spacje
JSON.stringify(data, null, 4) // wcięcie 4 spacje
JSON.stringify(data, null, '\t') // wcięcie tabulatorem
JSON.stringify(data) // zwarty: {"service":"payments","version":3,"active":true}Dlaczego JSON.stringify() zwraca undefined dla niektórych wartości?
JSON.stringify po cichu pomija właściwości obiektu, których wartości są undefined, funkcjami lub Symbolami — typy te nie mają reprezentacji JSON. Jeśli sama wartość najwyższego poziomu jest undefined, funkcja zwraca undefined (nie ciąg "undefined"). Używaj null zamiast undefined w opcjonalnych polach, które muszą pojawić się w wyjściu.
const event = {
traceId: "tr_9a2f",
handler: () => {}, // funkcja — pominięta
requestId: undefined, // undefined — pominięty
sessionId: Symbol("s"), // Symbol — pominięty
status: "ok"
}
JSON.stringify(event, null, 2)
// { "traceId": "tr_9a2f", "status": "ok" }Jak obsłużyć obiekty Date przy formatowaniu JSON?
Obiekty Date mają wbudowaną metodę toJSON(), która zwraca ciąg ISO 8601, więc JSON.stringify obsługuje je automatycznie. Nie potrzebujesz własnego replacera dla dat — zserializowana wartość będzie ciągiem w stylu "2026-03-10T14:22:00.000Z".
const order = {
orderId: "ord_8f2a91bc",
placedAt: new Date("2026-03-10T14:22:00Z"),
total: 42.98
}
JSON.stringify(order, null, 2)
// {
// "orderId": "ord_8f2a91bc",
// "placedAt": "2026-03-10T14:22:00.000Z",
// "total": 42.98
// }Jak sformatować ciąg JSON (nie obiekt) w JavaScript?
Najpierw sparsuj ciąg za pomocą JSON.parse(), a następnie ponownie zserializuj przez JSON.stringify(). Oba wywołania można połączyć w jeden wiersz do szybkiego debugowania.
const raw = '{"endpoint":"/api/v2/users","timeout":30,"retry":true}'
const formatted = JSON.stringify(JSON.parse(raw), null, 2)
console.log(formatted)
// {
// "endpoint": "/api/v2/users",
// "timeout": 30,
// "retry": true
// }Czy można używać JSON.stringify() w przeglądarce?
Tak. JSON jest wbudowanym globalnym obiektem w każdej nowoczesnej przeglądarce od IE8 — nie są potrzebne żadne tagi script ani importy. Otwórz konsolę DevTools i wywołaj JSON.stringify() bezpośrednio. Działa identycznie jak wersja Node.js, z tą samą sygnaturą parametrów i tymi samymi ograniczeniami dotyczącymi BigInt i odwołań cyklicznych.
// Działa w Chrome, Firefox, Safari, Edge — bez importów
const payload = { userId: "usr_7b3c", action: "checkout", cart: ["SKU-001", "SKU-002"] }
copy(JSON.stringify(payload, null, 2)) // copy() to funkcja pomocnicza DevToolsJavaScript daje pełną kontrolę — funkcje replacera, własne toJSON(), przetwarzanie dużych plików w Node.js. Gdy potrzebujesz tylko sprawdzić lub udostępnić sformatowany fragment, Formatter JSON od ToolDeck to najszybsza droga: wklej JSON i otrzymaj sformatowany, podświetlony wynik bez żadnej konfiguracji środowiska.
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.
Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.