Alles beginnt mit einer Netzwerkanfrage
Du sitzt an einer Anwendung, die irgendetwas tut, was sie nicht tun soll. Also öffnest du die DevTools, klickst auf den Network-Tab und lädst neu. Ein Dutzend Requests rauscht durch: Bilder, Skripte, Stylesheets. Und irgendwo dazwischen ist einer, der nicht wie die anderen aussieht. Kein Name mit .js oder .css am Ende. Vielleicht api/v1/users, vielleicht products?page=2. Du klickst ihn an, gehst auf den Response-Tab und siehst eine Textwand:
{
"users": [
{ "id": 42, "name": "Lea", "active": true },
{ "id": 43, "name": "Tom", "active": false }
],
"page": 1,
"total": 2
}
Was mich an diesem Moment immer wieder verblüfft: Es gibt nichts, was man erst lernen müsste. Auch ohne je von JSON gehört zu haben, verstehst du auf einen Blick, dass da eine Liste von zwei Nutzern steht — Lea mit der Nummer 42, aktiv, Tom, nicht aktiv. Das Format erklärt sich von selbst. Das ist keine Selbstverständlichkeit. Hinter diesem Request können Datenbankabfragen stehen, Caches, eine ganze Kette von Systemen. Aber zwischen den Systemen, dort, wo sie miteinander reden müssen, liegt dieser Text. Und beide Seiten verstehen ihn, ohne sich abzustimmen.
Diese Textwand ist heute die am weitesten verbreitete Datenserialisierung der Welt. Die meisten Webanwendungen sprechen sie, fast jede moderne API antwortet in ihr. Dabei hat alles ziemlich bescheiden angefangen.
Was JSON wirklich ist
Die Abkürzung steht für JavaScript Object Notation. Um das Jahr 2001 suchte Douglas Crockford, damals bei einem Startup namens State Software, das später von Yahoo übernommen wurde, nach einem Weg, wie zwei Systeme schnell Daten austauschen können. Dabei fiel ihm auf, dass JavaScript-Objekte eine erstaunlich natürliche Schreibweise haben: geschweifte Klammern, Doppelpunkte, Kommas. Eine Notation, die wie geschaffen dafür ist, eine Datenstruktur abzubilden. Crockford nahm diese Idee, schälte alles heraus, was nicht zwingend nötig war, und legte mit ein paar Seiten auf json.org eine Beschreibung vor, die man an einem Nachmittag durchlesen konnte.
Der Name ist ein kleines Unglück. JavaScript Object Notation klingt, als hätte das Format etwas mit JavaScript zu tun. Tut es kaum. Es stammt von JavaScript ab — die Syntax ist an Objektliterale angelehnt —, aber es ist ein reines Datenaustauschformat, mit keiner Programmiersprache verwoben. Es gibt JSON-Parser für praktisch jede Sprache, vom Mikrocontroller bis zur Mainframe, und keiner braucht JavaScript. Der Name blieb hängen, weil er zuerst da war.
Standardisiert wurde das Format mehrfach. 2013 veröffentlichte Ecma International die Norm ECMA-404. 2017 folgte RFC 8259 aus der Internet Engineering Task Force, der die älteren RFCs 7159 und 4627 ablöste. Wer die vollständigen Regeln nachlesen will, findet sie dort, zusammen mit der ehrlichen Einordnung, dass JSON aus JavaScript-Objektliteralen hervorgegangen ist. Wer es pragmatisch mag, hält sich an json.org — eine Website, die sich inzwischen wie ein Denkmal anfühlt und auf der das ganze Format problemlos auf einen Bildschirm passt.
Die eigentliche Leistung von JSON ist die Reduktion. Crockford hat eine Reihe von Entscheidungen getroffen: kein Schema, keine Kommentare, keine Namespaces, keine Erweiterbarkeit. Ein Objekt, ein Array, ein String, eine Zahl, ein Wahrheitswert — das ist alles. Die ganze Typphilosophie, die XML mit sich herumtrug, fiel ersatzlos weg. Übrig blieb ein Format, das ein Mensch in fünf Minuten liest und ein Computer in fünf Zeilen Code.
Warum es XML geschlagen hat
Um zu verstehen, warum JSON sich durchgesetzt hat, muss man kurz an das erinnern, was davor war. Die späten Neunziger und frühen Zweitausender gehörten XML. SOAP-Webservices beschrieben ihre Schnittstellen mit XML, Konfigurationsdateien waren XML, und wer ein Datum übermitteln wollte, schrieb ungefähr das hier:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:GetUser>
<tem:userId>42</tem:userId>
</tem:GetUser>
</soapenv:Body>
</soapenv:Envelope>
Lies das mal laut. Es ist ein Telefonbuch-Eintrag für eine einzelne Zahl. Und das war nicht einmal das Schlimmste. XML brachte endlose Debatten mit sich: Soll ein Zustand ein Attribut sein oder ein Kindelement? Wann braucht man einen Namespace, und warum heißt der Präfix soapenv so komisch? Um aus einem XML ein anderes XML zu machen, musste man XSLT lernen, eine eigene Transformationssprache. Und wer jemals ein XML-Schema von Hand gepflegt hat, kennt das Gefühl, für eine einfache Datenübermittlung eine kleine eigene Religion gegründet zu haben.
JSON war das Gegenprogramm. Die Datenstrukturen, die man im Speicher hat, haben in jeder gängigen Sprache ein direktes Gegenstück: Objekte werden zu Dictionaries, Hashmaps oder Assoziativ-Arrays, Arrays bleiben Arrays, Strings bleiben Strings. Es gibt keine Übersetzungsschicht, kein Mapping, keinen XSLT-Umweg. Du serialisierst, was du hast, und der Empfänger deserialisiert es zurück in seine Strukturen. Punkt.
Dazu kommt die Lesbarkeit ohne Schema. Bei XML musstest du, um eine fremde Datei zu verstehen, oft zuerst die DTD oder das XSD durcharbeiten. Bei JSON ist die Datei selbst die Dokumentation.
Und der größte Trumpf: Die Lernkurve ist praktisch null. Ich habe Leuten dabei zugesehen, wie sie das erste Mal eine JSON-Datei geöffnet und sofort verstanden haben, was drinsteht. Bei XML dauert das erste Verständnis auch nicht ewig, aber das Gefühl, es zu beherrschen, stellt sich nie so wirklich ein, weil immer noch eine Sonderregel mehr kommt.
Man darf ehrlich sagen: JSON ist kein schönes Format. Es hat keine Metadaten, keine Kommentare, keine Selbstbeschreibung. Es hat gewonnen, nicht weil es schön war, sondern weil alles andere nerviger war. SOAP war ein Bürokratie-Monster, XML-Schema eine Qual, und die schlankeren Alternativen der Zeit waren zu schwach für ernsthafte Daten. JSON lag genau in der Mitte: schlank genug, um angenehm zu sein, und mächtig genug, um alles darzustellen. Dazu kam ein günstiger Zeitpunkt: Die ersten großen Single-Page-Applications wurden in JavaScript geschrieben, und JavaScript konnte JSON nativ parsen — ohne Bibliothek, ohne zusätzlichen Request. Browser und Server sprachen plötzlich dieselbe Sprache.
Die sechs Datentypen
JSON kennt genau sechs Datentypen. Mehr nicht. Das klingt nach wenig, reicht aber für erstaunlich viel.
Ein Objekt ist eine Sammlung von Schlüssel-Wert-Paaren in geschweiften Klammern. Die Reihenfolge der Schlüssel ist laut Spezifikation nicht festgelegt, auch wenn die meisten Parser die Einfüge-Reihenfolge beibehalten. Die Schlüssel müssen Strings sein, doppelt quotiert:
{ "name": "Lea", "alter": 29 }
Ein Array ist eine geordnete Liste von Werten in eckigen Klammern. Es darf leer sein, und die Elemente können durchaus unterschiedlichen Typs sein:
[ "Montag", "Dienstag", 42, null, true ]
Ein String ist eine Zeichenkette in doppelten Anführungszeichen. Sonderzeichen werden mit Backslash maskiert, Unicode lässt sich über \uXXXX schreiben:
{ "gruß": "Hallo \"Welt\"", "herz": "❤" }
Eine Zahl ist eine Ganzzahl, eine Dezimalzahl oder eine Zahl mit Exponent. Es gibt keine führenden Nullen, keine Hexadezimalzahlen, kein NaN und kein Infinity:
{ "ganzzahl": -7, "dezimal": 3.14, "exponent": 6.022e23 }
Ein Boolescher Wert ist schlicht true oder false, klein geschrieben. Großschreibung ist ein Fehler:
{ "aktiv": true }
Und null ist der Wert für nichts. Kein undefined, kein None, kein nil — nur null:
{ "mittelname": null }
So weit die Theorie. In der Praxis sehen echte Daten selten so aufgeräumt aus. Hier ein realistisches, verschachteltes Beispiel — ein Nutzerdatensatz, wie ihn ein Backend zurückgeben könnte:
{
"id": 42,
"email": "lea@beispiel.de",
"profile": {
"vorname": "Lea",
"nachname": "Müller",
"sprachen": ["de", "en"]
},
"adressen": [
{ "typ": "privat", "plz": "10115", "stadt": "Berlin" },
{ "typ": "geschaeftlich", "plz": "20095", "stadt": "Hamburg" }
],
"registriertSeit": "2019-03-14T09:26:53Z",
"verifiziert": true,
"abteilung": null
}
Beachte: Das Datum ist ein String. JSON hat keinen eigenen Typ dafür — eine der ältesten Beschwerden über das Format. Und abteilung ist null, weil der Nutzer keiner Abteilung zugeordnet ist. So sieht realistische Datenmodellierung mit JSON aus: Objekte verschachteln sich, Arrays tauchen mitten in Objekten auf, die Typen wechseln von Feld zu Feld.
Die Stolperfallen der Syntax
JSON sieht harmlos aus, aber die Syntax ist strenger, als sie wirkt. Fast jede Gewohnheit, die man als JavaScript-Entwickler mitbringt, ist in JSON ein Fehler. Die Fallstricke im Einzelnen:
Schlüssel müssen doppelt quotiert sein. Ein unquotierter Schlüssel ist in JavaScript völlig normal, in JSON ein Syntaxfehler:
{ name: "Lea" } // Fehler
{ "name": "Lea" } // korrekt
Erweiterungen wie JSON5 erlauben unquotierte Schlüssel, reines JSON nicht. Bekommst du also eine Datei, deren Schlüssel keine Anführungszeichen haben, ist es kein reines JSON.
Strings nur mit doppelten Anführungszeichen. Einfache Quotes sind in JavaScript gültig, in JSON nicht. Wer Strings übernimmt und die Quotes zu konvertieren vergisst, scheitert am Parser.
Keine Kommentare. Wohl die Regel, an der sich die meisten stoßen. JSON erlaubt weder // noch /* */. Konfigurationsdateien, die man kommentieren möchte, müssen sich anders behelfen. Manche führen ein ignorierbares Feld wie "_comment" ein; das ist eine Konvention, keine Lösung.
Keine nachgestellten Kommas. Ein Komma nach dem letzten Element ist in JavaScript legitim und beliebt, weil es das Verschieben von Zeilen erleichtert. In JSON ist es ein Fehler:
[ 1, 2, 3, ] // Fehler
[ 1, 2, 3 ] // korrekt
Keine führenden Nullen. 042 ist ungültig, ebenso 01.5. Wer Zahlen aus CSV übernimmt, wo 042 vorkommen kann, muss aufpassen. Auch -0 ist ein Randfall: Die Syntax erlaubt es, aber ob der Parser -0 oder 0 zurückgibt, hängt von der Implementierung ab.
Doppelte Schlüssel sind erlaubt, das Verhalten ist undefiniert. Die Spezifikation erlaubt denselben Schlüssel mehrfach in einem Objekt, sagt aber nicht, was passieren soll. Die meisten Parser behalten den letzten Wert, aber das ist keine Garantie. Ich habe schon einen Nachmittag damit verbracht, herauszufinden, woher ein Wert kam, den niemand gesetzt hatte — es waren doppelte Schlüssel in einer Antwort.
Unicode und Escapes. Zeichen außerhalb von ASCII dürfen direkt als UTF-8 im Text stehen oder als \uXXXX-Escape. Zeichen außerhalb der Basic Multilingual Plane brauchen zwei Escapes, ein sogenanntes Surrogate Pair. Die vollständige Escape-Liste ist kurz und abschließend: \", \\, \/, \b, \f, \n, \r, \t, \u. Mehr gibt es nicht. Das \/ ist eine historische Kuriosität: Manche Serialisierer bauen es ein, damit eine Zeichenfolge wie </script> im JSON nicht versehentlich ein umschließendes <script>-Element in HTML beendet — ein Schutz, den viele gar nicht mehr kennen.
Die Syntax ist nicht schwer. Aber sie ist voller Verbote, die einen genau dort erwischen, wo man es nicht erwartet. Die gute Nachricht: Jeder ordentliche Parser meldet, wo der Fehler liegt, und man gewöhnt sich schnell daran, das falsche Komma auf einen Blick zu finden.
Ist JSON eine Teilmenge von JavaScript?
Die kurze Antwort: formal betrachtet fast, in der Praxis nein. Das führt zu Verwirrung, und die Verwirrung ist historisch gewachsen.
Als RFC 4627 das Format 2006 formalisierte, stand in der Spezifikation, JSON sei eine Teilmenge von JavaScript. Das stimmte weitgehend, aber nicht ganz. JavaScript-Objekte können mehr, als JSON erlaubt: einfache Quotes, nachgestellte Kommas, Kommentare, Funktionen als Werte, undefined, NaN, Infinity, Date-Objekte. Keines davon ist gültiges JSON. Und umgekehrt gibt es eine Stelle, an der JSON über eine naive Teilmenge hinausgeht: Die Zeichen U+2028 (Zeilenumbruch) und U+2029 (Absatzumbruch) sind in JSON-Strings erlaubt, galten in JavaScript-Quellcode aber lange als Zeilenenden. JSON.parse selbst hat damit kein Problem — das Parsen von JSON folgt einer eigenen Grammatik. Wer aber ein solches JSON in ein <script>-Element oder in einen JavaScript-String einbettete, konnte daran scheitern. Seit ES2019 sind die beiden Zeichen auch in JavaScript-Strings zulässig. Das Verhältnis ist also historisch, nicht streng.
Wichtig ist der Unterschied aus einem praktischen Grund. JSON.parse und JSON.stringify sind nicht dasselbe wie ein Objektliteral. Ein Objektliteral wird ausgewertet, es kann Code enthalten, Referenzen auf andere Objekte, Funktionen. Ein JSON-String ist bloß Text. Wenn du ein Date-Objekt stringify-erst, wird daraus ein String, keine Datumsrepräsentation. Und undefined verschwindet beim Stringify ganz — es gibt keine JSON-Entsprechung. Funktionen werden übersprungen, NaN und Infinity werden zu null. Wer das nicht weiß, wundert sich später, warum sein Objekt nach einem Roundtrip durch stringify und parse andere Felder hat als vorher. JSON ist nicht das Abbild eines JavaScript-Objekts; es ist ein eigenes Format mit eigenen Typen, das zufällig so aussieht wie ein Objektliteral.
Wo du ihm täglich begegnest
Wenn man erst anfängt, darauf zu achten, findet man JSON überall. Nicht nur in APIs.
REST-APIs. Die klassische Verwendung. Anfragen und Antworten der meisten HTTP-APIs sind JSON — als Request-Body, wenn du Daten schickst, und als Response, wenn du sie abholst. Im Browser sieht das so aus:
const res = await fetch("https://api.beispiel.de/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Lea" })
});
const neu = await res.json();
res.json() parst die Antwort, fertig. Mehr braucht ein Browser nicht.
Konfigurationsdateien. Kaum ein JavaScript-Projekt kommt ohne package.json aus, daneben stehen tsconfig.json, eslintrc.json oder .babelrc. Die Konfiguration des ganzen Node-Ökosystems ist JSON — oder JSON-artig mit Kommentaren, als JSONC.
localStorage. localStorage kann nur Strings speichern. Um ein Objekt abzulegen, serialisiert man es mit JSON.stringify und liest es mit JSON.parse wieder ein. Wer das nicht tut, speichert am Ende "[object Object]".
Datenbanken. PostgreSQL hat seit Jahren den Typ jsonb, mit dem sich JSON-Dokumente speichern, indizieren und durchsuchen lassen. MongoDB geht weiter: Sein Dokumentformat BSON ist eine binäre Variante von JSON, die den JSON-Typen Datum, Binärdaten und dezimale Zahlen hinzufügt. In gewissem Sinn ist ein großer Teil der NoSQL-Welt eine Antwort auf JSON.
JSON Lines (NDJSON). Für Streaming und Logs gibt es eine Zeile-pro-Objekt-Variante. Jede Zeile ist ein eigenständiges JSON-Dokument, ohne äußere Klammern:
{"ts":"2026-08-12T10:00:00Z","level":"info","msg":"gestartet"}
{"ts":"2026-08-12T10:00:01Z","level":"error","msg":"kaputt"}
Das erlaubt es, große Dateien zeilenweise zu streamen, ohne sie komplett in den Speicher zu laden, und Logeinträge hinten anzuhängen. Docker-Logs, CI-Systeme und Data-Pipelines sprechen das Format.
WebSockets. Nachrichten über WebSocket-Verbindungen sind oft JSON-Strings — eine kleine Struktur mit einem type-Feld und einem data-Feld. Das ist so verbreitet, dass es kaum noch auffällt.
Browser-APIs. Nicht nur fetch liefert JSON. Die DevTools zeigen es formatiert an, Browser-Erweiterungen kommunizieren damit, und selbst die Netzwerkanalyse speichert Requests als HAR-Dateien — eine JSON-Struktur, die man lesen kann, wenn man wissen will, was beim Laden einer Seite wirklich passiert ist.
Was alle diese Verwendungen gemeinsam haben: Es ist derselbe Text, dasselbe Parsing, dieselben Regeln. Ein Konfigurationsobjekt, eine API-Antwort und eine Log-Zeile gehorchen exakt derselben Syntax. Für ein Format, das ursprünglich nur eine schlanke Alternative zu XML sein sollte, ist das eine bemerkenswerte Eigenschaft.
So parsen es die verschiedenen Sprachen
Jede ernsthafte Sprache hat eine JSON-Bibliothek, und die APIs ähneln sich so sehr, dass man nach dem ersten Parser den Rest errät. Kurze, ehrliche Einblicke statt Tutorials.
JavaScript hat JSON nativ eingebaut, seit ECMAScript 5. JSON.parse(str) liefert ein Objekt, JSON.stringify(obj) einen String. Die einzige Falle ist die oben erwähnte: Werte ohne JSON-Entsprechung wie Funktionen oder undefined werden stillschweigend übersprungen oder zu null.
Python hat das json-Modul in der Standardbibliothek. json.loads() parst, json.dumps() serialisiert. Python macht aus JSON-Objekten standardmäßig Dictionaries, und die Typzuordnung ist sauber — bis auf die Zahlen: 42 wird zu einem int, 42.0 zu einem float. Pythons Ganzzahlen sind unbegrenzt, also bekommst du hier keine Präzisionsprobleme — aber der Empfänger am anderen Ende schon.
Java war lange ein Spezialfall. Die Standardbibliothek hatte kein JSON, also entstanden Dutzende Bibliotheken. Dominiert haben zwei: Jackson, das flexibel, schnell und überall im Spring-Universum eingebaut ist, und Gson, das kleiner und einfacher ist. Der Unterschied spielt sich meist in den Details ab: Wie behandelt die Bibliothek unbekannte Felder? Wirft sie bei null eine Exception?
Go hat encoding/json in der Standardbibliothek. Es arbeitet mit Strukturen und Feld-Tags: json:"name" legt fest, welchem JSON-Feld ein Struct-Feld entspricht. Die Eigenheit von Go: Der Parser meckert bei unbekannten Feldern nicht (es sei denn, man will es), und Zahlen landen ohne konkretes Ziel nach Go-Sitte in float64. Wer große Ganzzahlen über JSON bewegt, kennt das Problem.
Rust macht es mit serde. Man implementiert Deserialize und Serialize — meist per Ableitung — und serde_json übernimmt den Rest. Das ergibt eines der strengsten Verhalten überhaupt: Unbekannte Felder führen je nach Konfiguration zu Fehlern, Typen müssen exakt passen. Fluch und Segen zugleich: Bugs fliegen früh auf, aber man muss mehr deklarieren als anderswo.
Was alle gemeinsam haben: ein Parse-Schritt, ein Serialize-Schritt, eine Typabbildung, die man einmal kennen muss. Wer JSON in einer zweiten Sprache gelernt hat, lernt die dritte in einer Stunde.
Sicherheit: Lehren, teuer bezahlt
JSON hat eine erstaunlich düstere Sicherheitsgeschichte für ein Format, das so simpel ist. Fast jede Lektion wurde teuer bezahlt, und wer sie nicht kennt, wiederholt sie.
Das eval()-Desaster. In den frühen 2000ern gab es in JavaScript keinen eingebauten JSON-Parser. Die naheliegende Idee: JSON ist ja fast JavaScript, also warum nicht eval() auf den String loslassen? Das Ergebnis war eine Katastrophe. eval("(" + data + ")") führt Code aus — beliebigen Code. Eine manipulierte Antwort konnte böswillige Anweisungen in deiner Seite ausführen. Es dauerte eine Weile, bis sich herumsprach, dass man unvertrautes JSON niemals mit eval parsen darf. Die erste Welle von echten JSON-Parsern war im Kern eine Sicherheitskorrektur.
JSONP. Als Umweg um die Same-Origin-Policy wurde JSONP geboren: Man lädt Daten über ein <script>-Tag, das eine JSON-Datei mit einem Funktionsaufruf umhüllt — callback({"name":"Lea"}). Das umging die Browser-Beschränkungen, aber zu einem Preis: Jede Quelle, die JSONP ausliefert, kann beliebigen Code in deiner Seite ausführen. Man vertraute einer Website, nur um an ihre Daten zu kommen. Das Format gilt heute als überholt und gefährlich; moderne CORS-Konfiguration macht es überflüssig.
Die CSRF-Lücke durch Top-Level-Arrays. Eine besonders gemeine Falle. Liefert eine API als obersten Wert ein Array — etwa eine Liste von Kontakten —, kann ein Angreifer die Antwort per <script>-Tag einbinden, denn ein Array ist ein gültiges JavaScript-Programm. Mit Überschreibungen auf Array.prototype ließen sich Daten daraus absaugen. Die Lösung war denkbar einfach und wurde zur Faustregel: Top-Level-Werte müssen Objekte sein. Ein Objektliteral am Anfang eines Skripts ist nämlich ein Syntaxfehler — anders als ein Array. Das erste JSON-RFC (RFC 4627) erlaubte nur Objekte und Arrays an der Spitze; erst spätere Revisionen wie RFC 7159 und RFC 8259 öffneten das Format für jeden gültigen Wert, also auch Zahlen und Strings. Für Web-APIs ist ein Objekt an der Spitze bis heute der sichere Standard.
Prototype Pollution. Wenn ein Parser unbekannte Schlüssel stillschweigend übernimmt, kann jemand mit {"__proto__": {"isAdmin": true}} Objekte vergiften, die von JavaScript-Prototypen erben. Die Verteidigung: __proto__ und constructor.prototype nicht als normale Schlüssel behandeln, oder gleich strikt parsen.
JSON-Bomben. Tief verschachtelte oder riesige Dokumente können Parser und Server lahmlegen. Ein kleines Dokument, das Klammern in die Tiefe schachtelt, bringt einen rekursiven Parser zum Stack-Overflow; ein riesiges Dokument frisst Speicher. Wer unkontrollierte Größen akzeptiert, wird irgendwann von einem kaputten oder böswilligen Client in die Knie gezwungen.
Die moderne Regel ist kurz: Immer einen echten Parser verwenden, nie eval. Größen- und Tiefenlimits setzen. Keine Bibliothek einsetzen, die unbekannte Felder stillschweigend in Prototypen einträgt. Und JSON aus unvertrauten Quellen grundsätzlich wie Daten behandeln, nicht wie Code — was es ja auch ist.
Validierung und Erweiterungen: Schema, JSON5
JSON selbst validiert nichts. Du kannst das Format parsen, aber es sagt dir nicht, ob ein Dokument das richtige ist — ob eine E-Mail-Adresse fehlt, ob eine Zahl erlaubt ist, ob eine Liste die erwartete Struktur hat. Dafür gibt es JSON Schema. Es ist selbst eine JSON-Datei, die beschreibt, welche Felder erlaubt sind, welche Typen sie haben, was optional ist:
{
"type": "object",
"required": ["name", "email"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"email": { "type": "string", "format": "email" }
}
}
Mit einem JSON-Schema-Validator prüfst du Dokumente gegen diese Beschreibung, bevor du sie verarbeitest. Das ist besonders nützlich zwischen Teams: Der Vertrag ist maschinenlesbar, statt in Doku-Seiten versteckt zu sein. Manche Systeme generieren daraus sogar Formulare oder Dokumentation.
Daneben gibt es eine Familie von Erweiterungen, die alle aus demselben Frust entstanden sind: JSON verbietet Kommentare, nachgestellte Kommas und einfache Quotes. JSON5 erlaubt all das und noch mehr: unquotierte Schlüssel, Infinity und NaN, hexadezimale Zahlen. JSONC ist schlicht JSON mit Kommentaren, wie es der Konfigurationsbereich von Visual Studio Code praktisch verwendet. HJSON erlaubt Kommentare und eine laxere Syntax. Keine davon ist ein Standard. Sie sind pragmatische Hacks, entstanden, weil Entwickler das Gefühl hatten, dass JSON Konfigurationen für Menschen unnötig bestraft.
Ich nutze JSON5 gelegentlich für Konfigurationsdateien, weil Kommentare in Configs Gold wert sind. Aber ich würde nie eine API auf JSON5 aufsetzen. Wo Maschinen die einzigen Leser sind, zahlt sich Strenge aus.
Seine Grenzen und die Beschwerden
JSON ist gut, aber es fehlt ihm einiges, worüber man sich als Entwickler früher oder später beschwert. Die Liste der Klagen ist stabil, weil das Format bewusst klein gehalten wurde.
Keine Kommentare. Für Konfigurationsdateien ist das der größte Ärger. Eine tsconfig.json ohne Kommentare erklärt sich schlecht, und die Workarounds — ein _comment-Feld, das alle ignorieren müssen — sind genau das: Workarounds.
Kein Datentyp für Datum und Zeit. Kein Datum, keine Uhrzeit, kein Zeitstempel. Jedes Projekt erfindet sein Format neu, meist einen ISO-8601-String wie 2026-08-12T14:30:00Z, manchmal einen Unix-Timestamp. Deshalb existieren Bibliotheken wie date-fns oder Joda-Time, die im Kern nichts anderes tun, als diese Strings einheitlich zu interpretieren. Hätte JSON ein Datum, wären halb so viele Doku-Seiten nötig — aber man hätte sich auf ein Format und eine Zeitzonen-Darstellung einigen müssen, und genau das wollte man nicht.
Zahlenpräzision. JSON-Zahlen sind IEEE-754-Double. Ganzzahlen größer als 2^53 sind nicht zuverlässig darstellbar. Wer eine API für Kontodaten baut und eine Kontonummer oder einen Betrag in Cent schickt, bekommt unter Umständen eine falsche Zahl zurück. Die üblichen Workarounds: Geldbeträge als Dezimalstrings senden, große Zahlen als String. Wer einmal erlebt hat, wie eine Zahl stillschweigend eine andere wurde, lernt das Format an dieser Stelle zu misstrauen.
Keine Binärdaten. JSON kann nur Text. Bilder, Audio, Dateien müssen base64-kodiert werden, was die Daten um etwa ein Drittel aufbläht. Für echte Binärdaten ist JSON das falsche Werkzeug.
Tiefe Verschachtelung bricht Parser. Rekursive Parser werfen bei hinreichend tiefen Dokumenten einen Stack-Overflow. Die Spezifikation setzt keine Tiefengrenze; die Robustheit hängt von der Implementierung ab.
Doppelte Schlüssel. Erlaubt, Verhalten undefiniert. Liefert eine API versehentlich doppelte Schlüssel, entscheidet der Parser, welcher gewinnt — und verschiedene Parser entscheiden verschieden.
Und dann ist da das große Muster: Jeder, der JSON benutzt, erfindet die Räder neu, die JSON nicht mitliefert. Datumsformate, Paginierungs-Strukturen, Fehlerantworten, IDs. Es gibt keine Konventionen von JSON selbst, nur Konventionen von Frameworks und Projekten. Das ist die andere Seite der Einfachheit: JSON regelt wenig, und der Rest ist Verhandlungssache.
Die Alternativen
JSON ist nicht alternativlos, und je nach Anforderung sind die Alternativen besser. Ein ehrlicher Vergleich.
YAML ist das menschenfreundlichste der textbasierten Formate. Einrückung statt Klammern, Kommentare, alle Datentypen. Die Falle: YAML ist berüchtigt für seine Fallstricke. Tabs statt Leerzeichen brechen die Datei, das Norwegen-Problem macht aus no einen Boolean, und die Regeln zur automatischen Typumwandlung überraschen selbst Erfahrene. YAML ist stark, wo Menschen Dateien schreiben und lesen — CI-Konfigurationen, Docker-Compose-Files, Ansible-Playbooks. Für Maschine-zu-Maschine-Kommunikation ist es ungeeignet, weil das Parsen zu vieldeutig ist.
TOML ist ganz auf Konfigurationsdateien ausgerichtet. Von Grund auf dafür entworfen: klar, kommentierbar, mit einer Spezifikation, die man komplett lesen kann. Rusts Cargo nutzt es, viele moderne Tools folgen. Was TOML nicht kann: komplex verschachtelte Daten elegant darstellen. Für eine Config ist das egal, für eine API nicht.
Protocol Buffers sind der typbasierte Gegenentwurf. Du definierst ein Schema in einer .proto-Datei, generierst Code daraus, und die Nachrichten sind kompakt und typisiert. Schnelligkeit und Kompaktheit sind beeindruckend, aber du bezahlst mit einem Schema, das du pflegen musst, und einer Code-Generierung im Build. Wo Protobuf glänzt: interne Dienste mit hohem Durchsatz. Wo es stört: Wenn du ein Feld hinzufügst und Dienste neu kompilieren musst — wobei Versionierung hier viel abfedert.
MessagePack und CBOR sind binäre JSON-Formate. Sie bilden die JSON-Typen 1:1 ab, aber kompakt und ohne Textlast. CBOR ist in RFCs standardisiert und hat sich in Netzwerkprotokollen etabliert; MessagePack findet man oft in Redis- oder Cache-Schichten. Beide spielen dort, wo JSON zu groß oder zu langsam ist, das Datenmodell aber passt.
BSON ist die MongoDB-Variante: binär, mit zusätzlichen Typen wie Datum, Binärdaten und Dezimalzahlen. Es existiert praktisch nur für MongoDB, zeigt aber, wie sich die JSON-Grundidee für einen konkreten Bedarf erweitern lässt.
Wann welches Format? Für APIs und Web: JSON. Für Konfigurationen, die Menschen schreiben: TOML oder YAML. Für interne Dienste mit hohem Durchsatz: Protobuf. Für große oder binäre Daten in JSON-artiger Struktur: MessagePack oder CBOR. Wer diese Zuordnung kennt, spart sich eine Menge Frustration.
Schluss: Langweilige Formate leben länger
JSON ist seit rund 25 Jahren da und hat jeden Generationenwechsel der Webentwicklung überstanden: den Ajax-Boom, Single-Page-Applications, die REST-Welle, GraphQL, den Protobuf-Hype. Und es ist immer noch da. Warum? Aus denselben Gründen, aus denen es gewonnen hat. Es ist gut genug, es ist überall, und es ist todeinfach. Es löst kein Problem perfekt, aber es löst fast jedes Problem ausreichend. Es fehlt ihm an Eleganz, an Typsicherheit, an Konventionen — aber es fehlt ihm auch an Überraschungen, an Lernkurve, an Tooling-Zwang.
Man könnte sagen, JSON ist die langweiligste Entscheidung, die man beim Bauen eines Systems treffen kann. Genau das ist seine Stärke. Langweilig heißt: kein Vendor-Lock-in, keine Code-Generierung, keine Schema-Pflege, keine Spezial-Syntax, die man erklären muss. Jede Sprache parst es, jedes Tool versteht es, jeder Entwickler kennt es. Du wirst nie Probleme bekommen, weil du JSON gewählt hast — und das ist in der Welt der Datenformate eine unschlagbare Eigenschaft.
Greif zu etwas anderem, wenn du eine dieser Eigenschaften wirklich brauchst: Schema und Typsicherheit, maximale Kompaktheit, oder Konfigurationsdateien, die Menschen mit Kommentaren pflegen. Aber bevor du das tust, frage dich, ob das Problem das zusätzliche Tooling wert ist. Meistens ist es das nicht, und du nimmst wieder JSON.
Und wenn du das nächste Mal im Netzwerk-Tab der DevTools auf eine Textwand schaust, denk daran: Dieser Text ist ein Überlebender. Er hat die Namespace-Kriege überlebt, die eval-Apokalypse, ein halbes Dutzend Hype-Alternativen — und er wird vermutlich noch da sein, wenn die nächste Generation von Datenformaten längst wieder verschwunden ist. Manche Formate gewinnen, weil sie schnell sind oder elegant oder mächtig. JSON gewinnt, weil es schlicht zu unbequem ist, zu verschwinden.
