JSON Formatter JavaScript — JSON.stringify()

·Front-end & Node.js Developer·Sprawdzono przezMarcus Webb·Opublikowano

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.

Before · json
After · json
{"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.

JavaScript — minimalny działający przykład
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:

JavaScript — warianty parametru space
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",...}
Uwaga:Wartości 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

JavaScript — replacer tablicowy
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 wykluczone

Funkcja jako replacer — transformacja wartości

JavaScript — replacer funkcyjny
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
// }
Uwaga:Funkcja replacera jest wywoływana z 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.

JavaScript — serializacja Date
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.

JavaScript — własna metoda toJSON()
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"
// }
Uwaga: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

JavaScript — replacer dla BigInt
// 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

JavaScript — replacer dla odwołań cyklicznych
// 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 natywnie

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

Parametr
Typ
Domyślnie
Opis
value
any
Wartość do zserializowania. Obiekty, tablice, ciągi znaków, liczby, wartości logiczne i null są obsługiwane natywnie.
replacer
function | Array<string | number> | null
null
Filtruje lub transformuje pary klucz/wartość. Tablica = lista dozwolonych kluczy. Funkcja = wywoływana dla każdej pary klucz/wartość.
space
number | string | null
null
Wcięcie. Liczba = spacje na poziom (maks. 10). Ciąg = literalne wcięcie (np. "\t"). null lub 0 = zwarte 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

Node.js 18+ — przeformatowanie pliku JSON (ESM)
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)

Node.js 18+ — asynchroniczne przeformatowanie pliku
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().

JavaScript — fetch + czytelne wydrukowanie (Node.js 18+ lub przeglądarka)
// 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')
JavaScript — response.text() gdy potrzebujesz najpierw surowego ciągu
// 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ń.

bash — czytelne wydrukowanie pliku JSON za pomocą Node.js
# 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)))
"
bash — uniwersalne alternatywy
# 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}'
Uwaga:Uruchamiając Node.js w trybie ESM (np. z "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.

bash
npm install fast-json-stringify
JavaScript — fast-json-stringify dla zdarzeń telemetrii
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",...}'
Uwaga: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.

Node.js 18+ — util.inspect z kolorami
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 }))
Ostrzeżenie:Nie używaj wyjścia 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

bash
npm install stream-json
Node.js 18+ — stream-json dla dużych tablic
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.

Node.js 18+ — NDJSON z readline
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))
  }
}
Uwaga:Przełącz się z 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.

Zapominanie, że wartości undefined są po cichu wyrzucane

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.

Before · JavaScript
After · JavaScript
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żenia
const 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
// }
BigInt wyrzuca TypeError

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.

Before · JavaScript
After · JavaScript
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 BigInt
function 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"
// }
Odwołanie cykliczne powoduje awarię

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.

Before · JavaScript
After · JavaScript
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 JSON
import { 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)
Użycie space > 10 (po cichu obcięte)

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.

Before · JavaScript
After · JavaScript
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ęte
const 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.

Metoda
Czytelne wyjście
Prawidłowy JSON
Non-ASCII
Własne typy
Odwołania cykliczne
Wymaga instalacji
JSON.stringify
⚠️ przez replacer
✗ (błąd)
Nie
JSON.stringify + replacer
✅ pełna kontrola
Nie
util.inspect
✅ natywnie
Nie (wbudowane w Node)
fast-json-stringify
❌ tylko schemat
npm install
flatted
✗ (własny format)
✅ tylko cykliczne
npm install
jq (CLI)
N/A
N/A
Instalacja systemowa

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.

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

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

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

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

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

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

JavaScript 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

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

MW
Marcus WebbRecenzent techniczny

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.