Zum Inhalt springen
Toova
Alle Werkzeuge

7 JSON-Tricks, die Ihnen Stunden sparen

Toova

Jeder JavaScript-Entwickler verwendet JSON.parse und JSON.stringify dutzende Male pro Woche. Doch die meisten beschränken sich auf die Grundlagen — API-Antworten parsen und Objekte zu Strings serialisieren. Die API bietet mehr, und einige weniger genutzte Features lösen Probleme, die sonst Drittanbieter-Bibliotheken oder stundenlange Fehlersuche erfordern würden.

Dieser Leitfaden behandelt sieben Tricks, die über die Standardnutzung hinausgehen. Jeder davon ist sofort auf echte Codebasen anwendbar. Keine Abstraktionen, keine erfundenen Beispiele — das sind Muster, die in Produktionssystemen vorkommen.

Sie können alle Codebeispiele in diesem Artikel mit dem Toova JSON Formatter überprüfen und erkunden, der JSON vollständig im Browser validiert und formatiert.

1. Deep Clone mit JSON-Roundtrip (und seinen Grenzen)

Der älteste Trick im JavaScript-Repertoire: JSON.parse(JSON.stringify(obj)) verwenden, um einen Deep Clone eines einfachen Objekts zu erstellen.

const original = { a: 1, b: { c: 2 } };

// Naive approach — deeply nested objects are still shared
const shallowCopy = { ...original }; // b still points to same object

// JSON deep clone — creates a completely independent copy
const deepClone = JSON.parse(JSON.stringify(original));

deepClone.b.c = 99;
console.log(original.b.c); // 2 — original untouched

Dies funktioniert, weil die Serialisierung zu einem String und das anschließende Parsen einen völlig neuen Objektbaum ohne gemeinsame Referenzen erstellt. Es ist schnell, erfordert keine Abhängigkeiten und ist seit ES5 verfügbar.

Die moderne Alternative für die In-Memory-Nutzung ist structuredClone():

// Modern alternative: structuredClone() — handles more types
// Supported in Node.js 17+ and all evergreen browsers
const clone = structuredClone(original);

Kennen Sie die Grenzen des JSON-Ansatzes, bevor Sie sich darauf verlassen:

// JSON.parse/stringify CANNOT handle:
const broken = {
  date: new Date(),      // becomes a string — loses Date prototype
  fn: () => 'hello',    // dropped silently
  undef: undefined,     // dropped silently
  inf: Infinity,        // becomes null
  map: new Map(),       // becomes {}
  cycle: null,          // circular refs throw
};

Enthält Ihr Objekt ausschließlich einfache Daten — Strings, Zahlen, Booleans, Arrays und verschachtelte einfache Objekte — ist der JSON-Roundtrip sicher und schnell. Für alles andere bevorzugen Sie structuredClone() oder eine dedizierte Bibliothek.

2. Custom Replacer zum Filtern und Maskieren

Die meisten Entwickler wissen, dass JSON.stringify ein zweites Argument akzeptiert, nutzen es aber selten. Dieses zweite Argument ist der Replacer: entweder ein Array von Schlüsseln, die eingeschlossen werden sollen, oder eine Funktion, die genau steuert, wie jeder Wert serialisiert wird.

Array-Replacer — bestimmte Schlüssel auf die Whitelist setzen:

const user = {
  id: 'u_001',
  name: 'Alice',
  password: 'hunter2',        // must not appear in logs
  creditCard: '4111111111111111', // must not appear in logs
  role: 'admin',
};

// Replacer as array: only include these keys
JSON.stringify(user, ['id', 'name', 'role']);
// '{"id":"u_001","name":"Alice","role":"admin"}'

Funktions-Replacer — Werte transformieren oder schwärzen:

// Replacer as function: full control over key/value
const masked = JSON.stringify(user, (key, value) => {
  if (key === 'password' || key === 'creditCard') return '[REDACTED]';
  if (key === '' ) return value; // root object — always return
  return value;
});
// '{"id":"u_001","name":"Alice","password":"[REDACTED]","creditCard":"[REDACTED]","role":"admin"}'

Eine ausgefeiltere Version maskiert Werte basierend auf ihrer Form statt ihrem Schlüsselnamen:

// Replacer for type-based masking
const sanitize = (key, value) => {
  if (typeof value === 'string' && value.match(/^4[0-9]{15}$/)) {
    return '****-****-****-' + value.slice(-4);
  }
  return value;
};

Diese Technik ist unverzichtbar für Logging-Middleware: Sie möchten strukturierte Logs mit vollem Objektkontext, aber bestimmte Felder dürfen niemals einen Log-Aggregator erreichen. Der Replacer ermöglicht es Ihnen, dies an der Serialisierungsgrenze zu handhaben, anstatt Schwärzungslogik über die gesamte Codebasis zu verstreuen.

3. Schlüssel deterministisch sortieren

Die Reihenfolge der Objektschlüssel in JavaScript ist die Einfügungsreihenfolge (für String-Schlüssel). Zwei Objekte mit denselben Schlüsseln, aber in unterschiedlicher Reihenfolge erstellt, erzeugen unterschiedliche JSON-Strings — das bricht naive Gleichheitsprüfungen, Cache-Schlüssel und Inhalts-Hashes.

function sortedStringify(obj) {
  return JSON.stringify(obj, Object.keys(obj).sort());
}

const a = { z: 1, a: 2, m: 3 };
const b = { a: 2, m: 3, z: 1 };

sortedStringify(a) === sortedStringify(b); // true — key order normalized

Für tief verschachtelte Objekte wenden Sie die Sortierung rekursiv an:

// Recursive key sorting for nested objects
function sortKeys(value) {
  if (Array.isArray(value)) return value.map(sortKeys);
  if (value !== null && typeof value === 'object') {
    return Object.fromEntries(
      Object.keys(value).sort().map((k) => [k, sortKeys(value[k])])
    );
  }
  return value;
}

const sorted = JSON.stringify(sortKeys(deepNested));

Sortiertes JSON ist unerlässlich, wenn Sie:

  • Cache-Schlüssel aus Request-Bodies generieren
  • Prüfsummen oder Signaturen über JSON-Payloads berechnen
  • API-Antworten in Tests unabhängig von der Feldreihenfolge vergleichen
  • Konfigurationsobjekte speichern, bei denen die Reihenfolge die Gleichheit nicht beeinflussen soll

Nach dem Sortieren und Pretty-Printing verwenden Sie das Text-Diff-Tool, um zwei normalisierte JSON-Strings zu vergleichen und genau zu sehen, welche Werte sich zwischen Versionen geändert haben.

4. BigInt ohne Genauigkeitsverlust behandeln

Der JavaScript-Typ Number kann Integer sicher bis zu 253 − 1 darstellen. Für IDs, die von verteilten Systemen generiert werden, Finanzbeträge in kleinsten Einheiten oder Zeitstempel in Nanosekunden reicht das nicht. BigInt deckt ganzzahlige Zahlen beliebiger Genauigkeit ab, aber JSON.stringify weiß nicht, wie es damit umgehen soll.

const data = {
  amount: 9007199254740993n, // larger than Number.MAX_SAFE_INTEGER
};

// This throws: TypeError: Do not know how to serialize a BigInt
JSON.stringify(data); // ERROR

Die Standard-Workarounds:

// Solution 1: Convert to string with a replacer
JSON.stringify(data, (key, value) =>
  typeof value === 'bigint' ? value.toString() : value
);
// '{"amount":"9007199254740993"}'

// Solution 2: toJSON() on the BigInt prototype (monkey-patch — use with caution)
BigInt.prototype.toJSON = function () { return this.toString(); };
JSON.stringify(data); // '{"amount":"9007199254740993"}'

Auf der Parse-Seite kann eine Reviver-Funktion BigInt-Werte aus ihrer String-Repräsentation wiederherstellen:

// Reviver to restore BigInt on parse
const revived = JSON.parse('{"amount":"9007199254740993"}', (key, value) => {
  if (key === 'amount') return BigInt(value);
  return value;
});
console.log(typeof revived.amount); // 'bigint'

Halten Sie die BigInt-zu-String-Konvertierung in einer gemeinsamen Serialisierungsschicht, damit sie konsistent angewendet wird. Wenn BigInts zu ad-hoc-JSON.stringify-Aufrufen an den Rändern einer Codebasis durchsickern, entstehen unvorhersehbare Fehler, die schwer zu verfolgen sind.

5. Erkennung zirkulärer Referenzen

Eine zirkuläre Referenz tritt auf, wenn ein Objekt einen Verweis auf sich selbst oder auf einen Vorfahren im Objektgraph enthält. Sie sind häufiger als man denkt: Event-Emitter, DOM-Knoten, React-Fiber-Knoten und ORM-Entitäten haben häufig Rückverweise.

// Circular reference example
const obj = { name: 'node' };
obj.self = obj; // obj references itself

JSON.stringify(obj); // throws: TypeError: Converting circular structure to JSON

Behandeln Sie dies mit einem benutzerdefinierten Replacer, der besuchte Objekte verfolgt:

// Manual circular reference handler
function safeStringify(obj) {
  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;
  });
}

safeStringify(obj); // '{"name":"node","self":"[Circular]"}'

Das WeakSet ist hier die richtige Datenstruktur: Es hält Objektreferenzen, ohne die Garbage Collection zu verhindern, und die Suche ist O(1). Dieses Muster funktioniert auch in Logging-Middleware, wo Fehler gracefully degradieren sollen, anstatt während der Serialisierung eine Ausnahme zu werfen.

Eine häufige Variante: Anstatt zirkuläre Referenzen als [Circular] zu markieren, ersetzen Sie sie durch einen Pfad-String wie [Circular: $.config.parent], um den Referenzort beim Debuggen explizit zu machen.

6. Arrays in einer Zeile formatieren

Das dritte Argument von JSON.stringify ist der Einzug. Das Übergeben von 2 oder 4 expandiert alles auf mehrere Zeilen, was für Objekte gut ist, aber für Arrays von Primitiven wie Tags, IDs oder Koordinaten ausschweifend sein kann.

const mixed = {
  title: 'Report',
  tags: ['json', 'api', 'debug'],
  config: { indent: 2, sortKeys: true },
  count: 42,
};

// Default: everything multi-line
JSON.stringify(mixed, null, 2);
// {
//   "title": "Report",
//   "tags": [
//     "json",
//     "api",
//     "debug"
//   ],
//   "config": {
//     "indent": 2,
//     "sortKeys": true
//   },
//   "count": 42
// }

Sie können die Ausgabe nachbearbeiten, um einfache Arrays in einer Zeile zusammenzufassen:

// Custom replacer for single-line arrays
function prettyMixed(obj) {
  const raw = JSON.stringify(obj, null, 2);
  // Collapse arrays that contain only primitives onto one line
  return raw.replace(
    /\[\n\s+([\s\S]*?)\n\s+\]/g,
    (match, inner) => {
      const items = inner.split(',\n').map((s) => s.trim());
      if (items.every((s) => !/^[{\[]/.test(s))) {
        return '[' + items.join(', ') + ']';
      }
      return match;
    }
  );
}

Das Ergebnis: Objekte bleiben mehrzeilig für die Lesbarkeit, Arrays von Primitiven werden für Kompaktheit auf eine Zeile zusammengefasst. Dies ist das Format, das in vielen Konfigurationsdateien und strukturierten Log-Ausgaben verwendet wird, wo sowohl Menschen als auch Maschinen dieselben Daten lesen müssen. Der JSON Formatter und der JSON-zu-YAML-Konverter handhaben dieses Zusammenfassen automatisch.

7. Großes JSON streamen

Eine große JSON-Datei vollständig in den Speicher zu laden, bevor man sie parst, ist der häufigste Performance-Fehler in JSON-Verarbeitungspipelines. Eine 100-MB-JSON-Antwort reserviert mindestens 100 MB Heap für den rohen String, dann eine zweite Zuweisung für das geparste Objekt. Bei Dateien über einigen Megabyte verarbeitet ein Streaming-Parser die Daten inkrementell.

Unter Node.js mit einer Streaming-Bibliothek:

// node --experimental-vm-modules (or use a streaming JSON lib)
// Native streaming with the JSON Source Map proposal (Stage 3, 2026):

// For now, use a library like @streamparser/json or jsonstream2
import { createReadStream } from 'fs';
import parser from '@streamparser/json';

const jsonParser = new parser.JSONParser();
jsonParser.onValue = ({ value, key, parent }) => {
  if (key === 'id') {
    console.log('Found id:', value);
  }
};

createReadStream('large.json').pipe(jsonParser);

Im Browser mit der Fetch Streams API:

// Browser-side streaming with the Fetch + JSON stream decoder:
const response = await fetch('/api/large-data');
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
  // Process complete JSON objects from buffer...
}

Streaming ist am nützlichsten in Batch-Verarbeitungspipelines, großen Export-Endpunkten und Log-Analyseskripten. Für typische API-Antworten unter 1 MB ist das Standard-JSON.parse schnell genug und weitaus einfacher. Die Schwelle, ab der Streaming in der Praxis zu zahlen beginnt, liegt bei etwa 5–10 MB, abhängig von der Komplexität der geparsten Struktur und der Heap-Größe der Zielumgebung.

Um die Struktur großer JSON-Antworten zu erkunden, bevor Sie einen Streaming-Parser erstellen, fügen Sie ein Beispiel in den JSON Formatter ein, um zu sehen, welche Pfade die benötigten Daten enthalten, und konvertieren Sie dann zu CSV oder YAML für weitere Analysen.

Zusammenfassung

Diese sieben Techniken decken die wirkungsvollsten Bereiche der JSON-API ab:

  • Deep Clone via Roundtrip — schnell für einfache Daten, Grenzen kennen
  • Replacer — Schlüssel filtern und sensible Werte an der Serialisierungsgrenze maskieren
  • Schlüsselsortierung — deterministische Ausgabe für Cache-Schlüssel, Signaturen und Test-Assertions
  • BigInt-Behandlung — zu String serialisieren, beim Parsen wiederherstellen
  • Erkennung zirkulärer Referenzen — WeakSet-basierter Replacer für sichere Serialisierung
  • Pretty-Print mit kompakten Arrays — menschenlesbare Ausgabe ohne übermäßige Leerzeichen
  • Großes JSON streamen — inkrementelles Parsen für große Dateien und API-Antworten

Die vollständige Dokumentation der Replacer- und Space-Argumente von JSON.stringify finden Sie in der MDN JSON.stringify-Referenz. Die moderne Deep-Clone-Alternative ist in der structuredClone()-Dokumentation beschrieben, die alle unterstützten Typen und Grenzfälle abdeckt.