JSON RFC 8259: subtile Details, die Produktions-Parser stolpern lassen

JSON ist kleiner, als du denkst

Die JSON-Grammatik in RFC 8259 ist winzig: Objekte, Arrays, Strings, Zahlen und die drei Literale true, false und null. Diese Einfachheit ist der Grund, warum es überall ist — und auch, warum zwei Überraschungen die Leute kalt erwischen. Erstens kann jeder Wert das ganze Dokument sein: 42, "hello" und true sind jeweils vollständiges, gültiges JSON. Das ältere RFC 4627 verlangte, dass die oberste Ebene ein Objekt oder Array ist, also lehnen manche Alt-Tools einen nackten Wert noch ab.

Zweitens lässt die Spezifikation mehrere Verhaltensweisen bewusst unspezifiziert. Sie definiert die Syntax präzise, sagt aber wenig über doppelte Schlüssel, Zahlengenauigkeit oder Reihenfolge. Produktions-Parser füllen diese Lücken unterschiedlich, und genau dort wohnen die Interoperabilitäts-Bugs.

Doppelte Schlüssel: undefiniertes Verhalten

RFC 8259 sagt, dass Objektnamen eindeutig sein SOLLTEN (SHOULD), nicht MÜSSEN (MUST). Wiederholt sich ein Schlüssel, ist das Ergebnis unspezifiziert, also weichen Parser ab: Die meisten nehmen den letzten Wert, manche behalten den ersten, einige werfen einen Fehler, und ein seltener behält beide. Keiner ist laut Spec falsch.

Das ist ein echtes Sicherheitsrisiko. Validiert ein Dienst {"role":"user","role":"admin"}, indem er das erste role liest, während ein anderer auf das letzte handelt, kann ein Angreifer ein Privileg an der Validierung vorbeischmuggeln. Behandle doppelte Schlüssel als Fehler, wo die Entscheidung zählt, und verlasse dich nie darauf, welcher Wert 'gewinnt'.

Zahlen: keine Ganzzahlen, nur Ärger

JSON hat einen einzigen Zahlentyp. Die RFC definiert weder Genauigkeit noch Bereich — sie merkt nur an, dass Implementierungen zur Interoperabilität erwartungsgemäß Werte handhaben, die in IEEE-754-Doppelpräzision passen. In der Praxis heißt das, dass alles über 2^53 (also 9007199254740992) Genauigkeit verlieren kann.

Der klassische Bug: Eine 64-Bit-ID wie 9007199254740993 wird nach JSON.parse in JavaScript zu 9007199254740992, weil Zahlen Doubles sind. Die Lösung ist, große Ganzzahlen als Strings zu führen. Ein paar weitere oft vergessene Regeln: keine führenden Nullen (007 ist ungültig), kein führendes +, kein abschließender Dezimalpunkt (1. ist ungültig), und NaN und Infinity sind kein gültiges JSON — serialisiere sie als null oder String.

Strings, Unicode und Kodierung

Strings müssen doppelte Anführungszeichen verwenden, und Steuerzeichen von U+0000 bis U+001F müssen escaped werden. Der Schrägstrich darf optional als \/ escaped werden, weshalb manche Encoder <\/script> ausgeben — eine legale Art, ein HTML-Script-Tag nicht zu zerbrechen. RFC 8259 schreibt UTF-8 für JSON vor, das zwischen Systemen ausgetauscht wird.

Zeichen außerhalb der Basic Multilingual Plane (BMP), etwa die meisten Emojis, werden als Surrogatpaar aus \u-Escapes geschrieben, nicht als einzelnes. Einzelne, ungepaarte Surrogate sind technisch ungültig, doch manche Parser akzeptieren sie — eine Portabilitätsfalle, die erst auftaucht, wenn deine Daten in eine striktere Implementierung wandern.

Dinge, die wie JSON aussehen, aber keins sind

Die häufigsten Fehler entstehen, wenn man JSON wie JavaScript behandelt. Nachgestellte Kommas sind ungültig. Kommentare gibt es in JSON nicht — JSON5 und JSONC fügen sie hinzu, aber ein strikter RFC-8259-Parser weist sie zurück. Einfach-gequotete Strings und nicht gequotete Objektschlüssel sind ebenfalls ungültig, so vertraut sie auch aussehen.

Subtiler ist die Byte-Order-Mark (BOM). RFC 8259 sagt, Produzenten DÜRFEN KEIN (MUST NOT) BOM hinzufügen und Konsumenten DÜRFEN (MAY) eines ignorieren, aber die BOM ist nicht Teil von JSON, also kann eine UTF-8-BOM am Dateianfang einen strikten Parser am allerersten Byte scheitern lassen. Unbedeutender Leerraum beschränkt sich auf Leerzeichen, Tab, Zeilenvorschub und Wagenrücklauf — sonst nichts.

Erkenntnisse

Weise doppelte Schlüssel zurück, wo eine Sicherheits- oder Korrektheitsentscheidung vom Wert abhängt.

Führe 64-Bit-Ganzzahlen als Strings, damit sie Doppelpräzisions-Parser wie den von JavaScript überleben.

Verlasse dich nicht auf die Reihenfolge der Objektschlüssel — die Spec definiert Objekte als ungeordnet, auch wenn die meisten Parser die Einfügereihenfolge bewahren.

Denk daran, dass unter RFC 8259 jeder Wert gültiges Top-Level-JSON ist, nicht nur Objekte und Arrays.

Willst du Kommentare oder nachgestellte Kommas, willst du JSON5/JSONC — konvertiere, bevor ein strikter Parser die Daten sieht. Der JSON-Formatierer auf dieser Seite validiert gegen die strikte RFC-8259-Grammatik, sodass diese Probleme sofort auftauchen.