Zum Inhalt springen
jsonbeautifiers
Deutsch

Die zehn JSON-Fehler, die wirklich Payloads zerlegen

Nach Häufigkeit sortiert, nicht danach, wie interessant sie sind, mit der genauen Fehlermeldung, die jeder erzeugt.

Jede Aussage auf dieser Seite ist entweder gemessen oder belegt. Wo sie keines von beidem ist, steht das dabei.

Fast jedes „ungültiges JSON“-Ticket ist eines von zehn Dingen. Der Parser nennt Ihnen eine Position, gelegentlich ein Zeichen, und nie die Ursache. Hier sind sie in grober Reihenfolge ihrer Häufigkeit, mit der Meldung, die Sie gesehen haben werden, und der Lösung.

1. Nachgestellte Kommas

Mit großem Abstand das häufigste, weil jedes andere Format, das Sie den ganzen Tag schreiben, sie erlaubt.

{ "a": 1, "b": 2, }

V8 gibt Ihnen eine Meldung, die weder Kommas noch das erwähnt, was Sie getan haben:

Expected double-quoted property name in JSON at position 18 (line 1 column 19)

Der Parser hat das Komma konsumiert, einen weiteren Schlüssel erwartet und ist auf } gestoßen. Machen Sie jetzt dasselbe in einem Array:

Unexpected token ']', "[1,2,]" is not valid JSON

Völlig andere Formulierung für denselben Fehler, weil der Array-Pfad im Parser an einer anderen Grammatikregel scheitert. Wenn Sie nach dem Fehlertext suchen, um herauszufinden, was passiert ist, ist diese Asymmetrie der Grund, warum Sie nichts Brauchbares finden.

Python ist deutlicher, aber erst seit Kurzem. Ab 3.13:

Illegal trailing comma before end of object

In 3.12 und früher liefert dieselbe Eingabe Expecting property name enclosed in double quotes: line 1 column 19 (char 18). Derselbe Interpreter, derselbe Fehler in Ihrer Datei, zwei verschiedene Erklärungen, je nachdem, welche Version Ihre CI gerade festnagelt.

Lösung: Löschen Sie das Komma. { "a": 1, "b": 2 }.

2. Einfache Anführungszeichen

Ein Python-Dict, das durch print() oder str() gelaufen ist statt durch json.dumps():

{'ok': True}

Das ist kein JSON und war es nie. Es scheitert am ersten Anführungszeichen:

Expected property name or '}' in JSON at position 1 (line 1 column 2)

Beachten Sie auch True, ein zweiter, eigener Fehlschlag, der hinter dem ersten wartet. JSON-Wahrheitswerte sind kleingeschrieben.

{"ok": true}

Beheben Sie es an der Quelle: json.dumps(obj), und wenn die Ausgabe in eine UTF-8-Datei oder einen HTTP-Body geht, json.dumps(obj, ensure_ascii=False), damit Zeichen mit Diakritika lesbar bleiben, statt zu \uXXXX-Escapes zu werden. Dazu separators=(",", ":"), wenn Sie es kompakt wollen.

3. Schlüssel ohne Anführungszeichen

Ein JavaScript-Objektliteral, direkt in ein JSON-Feld eingefügt:

{ name: "ada", active: true }
Expected property name or '}' in JSON at position 2 (line 1 column 3)

JSON verlangt, dass jeder Schlüssel eine Zeichenkette in doppelten Anführungszeichen ist. Nicht in einfachen, nicht nackt, keine Zahl. {"name": "ada", "active": true}. Das ist dieselbe Sorte Einfügung wie zuvor, und die Lösung ist dieselbe: Holen Sie den Wert mit einem echten Serialisierer aus der Laufzeit statt aus einem Konsolen-Log.

4. Nicht escapte Steuerzeichen

Ein echter Zeilenumbruch innerhalb eines Zeichenkettenliterals:

{"note": "line one
line two"}
Bad control character in string literal in JSON at position 18 (line 1 column 19)

Python nennt es Invalid control character at: line 1 column 19 (char 18). So oder so sagt Ihnen der Parser, dass ein Zeichen unterhalb von U+0020 innerhalb einer Zeichenkette aufgetaucht ist, wo nur sein Escape erlaubt ist.

{"note": "line one\nline two"}

Tabulatoren sind dasselbe Problem und schwerer zu sehen, weil ein in einen Wert eingefügter Tabulator wie Leerzeichen aussieht. Die Ursache ist fast immer JSON, das per Zeichenkettenverkettung zusammengebaut wurde, wobei ein Feld mit einem Zeilenumbruch wörtlich eingesetzt wird. Der letzte Abschnitt behandelt das richtig.

5. Windows-Pfade

{"path": "C:\Users\ada\config.json"}

\U und \a sind keine gültigen Escapes. Python ist explizit: Invalid \escape: line 1 column 13 (char 12). V8 sagt Bad escaped character in JSON at position 13 (line 1 column 14) und zeigt auf das U statt auf den Backslash. Die neun legalen Escapes sind \" \\ \/ \b \f \n \r \t und \uXXXX. Alles andere ist ein Fehler, was das richtige Design ist und ständig überrascht.

{"path": "C:\\Users\\ada\\config.json"}

Normale Schrägstriche funktionieren unter Windows in fast jeder API einwandfrei und kosten Sie keine verdoppelten Backslashes. Wenn Sie einen Textblock einbetten müssen und das lieber nicht von Hand tun, erledigt das das Escape-Werkzeug, und Unescape geht in die andere Richtung.

6. Unsichtbare Zeichen

Das ist der Fehler, der einen Nachmittag frisst. Zwei Varianten:

Geschütztes Leerzeichen (U+00A0). Kopieren Sie einen Ausschnitt aus einer Dokumentationsseite, einem Chat-Client oder einem PDF, und die Leerzeichen zwischen den Token sind womöglich keine Leerzeichen. RFC 8259 erlaubt zwischen Token genau vier Leerraumzeichen: Leerzeichen, Tabulator, Wagenrücklauf und Zeilenvorschub. U+00A0 gehört nicht dazu, es ist also ein Syntaxfehler, und es sieht genauso aus wie das Zeichen daneben.

Typografische Anführungszeichen. Word und Google Docs korrigieren das gerade Anführungszeichen U+0022 beim Tippen automatisch in das typografische Paar U+201C und U+201D. JSON akzeptiert nur U+0022. Ein Dokument, das auf dem Bildschirm tadellos in Anführungszeichen steht, enthält überhaupt keine Zeichenketten-Begrenzer.

Keine der beiden Varianten erzeugt eine Meldung, die den Codepoint nennt. Je nachdem, wo das Zeichen landet, bekommen Sie Expected double-quoted property name in JSON at position 8 oder ein Unexpected token ' ', das Ihnen ein Zeichen zurückdruckt, das Sie von einem normalen Leerzeichen nicht unterscheiden können. Fügen Sie das Dokument in den Validator ein: Er nennt das Zeichen und seinen Codepoint am genauen Offset, was der schnellste Weg ist, es zu finden. JSON reparieren entfernt sie und sagt Ihnen, was es entfernt hat.

7. Kommentare

{
  // der Anzeigename des Nutzers
  "name": "ada"
}

V8 meldet Expected property name or '}' in JSON at position 4 (line 2 column 3). Python bricht ab mit Expecting property name enclosed in double quotes: line 2 column 3 (char 4). Beide zeigen auf den Schrägstrich, und keiner sagt das Wort Kommentar, sodass sich die Meldung wie ein Anführungszeichen-Problem in einer Zeile liest, die gar keine Zeichenketten enthält.

JSON hat keine Kommentarsyntax. Crockford hat sie bewusst entfernt, weil Leute Kommentare benutzten, um Parser-Direktiven zu transportieren. Wenn Sie den Konsumenten kontrollieren, erlaubt JSONC (was VS Code für seine eigenen Einstellungen benutzt) Kommentare und nachgestellte Kommas, und JSON5 erlaubt erheblich mehr. Wenn nicht, verschieben Sie die Prosa in ein Feld oder in das Schema, wo Beschreibungen hingehören. Die vollständige Begründung ist zehn Minuten wert, wenn Sie gerade ein Konfigurationsformat auswählen.

8. NaN und Infinity

{"ratio": NaN}
Unexpected token 'N', "{"ratio": NaN}" is not valid JSON

Die Falle ist, dass Python das standardmäßig ausgibt. json.dumps({"ratio": float("nan")}) erzeugt {"ratio": NaN} und löst nichts aus, weil der Encoder von CPython bewusst nachsichtig ist und sein eigener Decoder den Wert wieder annimmt. Jeder Nicht-Python-Konsument weist ihn zurück.

json.dumps(obj, allow_nan=False)   # löst ValueError aus, statt ungültiges JSON zu verschicken

Schalten Sie das heute in Ihrer Serialisierungsschicht ein. Ein NaN, das die Produktion erreicht, ist eine Division, die Sie nicht abgesichert haben, und Sie finden sie lieber am Encoder als im Parser eines Kunden.

9. Doppelte Schlüssel

{"id": 1, "id": 2}

Überhaupt kein Fehler. RFC 8259 sagt, Schlüssel SOLLTEN eindeutig sein, und lässt das Verhalten undefiniert, wenn sie es nicht sind. JavaScript und Python behalten beide den letzten, das parst also zu {"id": 2} und Ihr erster Wert ist spurlos weg. Andere Parser behalten den ersten, manche lösen einen Fehler aus. Das ist der einzige Punkt auf der Liste, der stumm bleibt, und damit der schlimmste. Schicken Sie ein Payload durch den Validator, der Duplikate meldet, statt sie stillschweigend zusammenzufalten.

10. Zahlen

Zwei Fehlschläge teilen sich diesen Platz.

Führende Nullen. {"code": 007} ist ungültig. Die JSON-Grammatik erlaubt eine einzelne 0 oder eine Ziffer von 1 bis 9 gefolgt von weiteren Ziffern, und sonst nichts. Eine Postleitzahl, eine Ländervorwahl oder eine Teilenummer mit führender Null ist eine Zeichenkette. {"code": "007"}.

Ganze Zahlen über 2^53-1. {"id": 12345678901234567890} parst problemlos und kommt als andere Zahl zurück, weil JavaScript sie als IEEE-754-Double speichert und Number.MAX_SAFE_INTEGER bei 9007199254740991 liegt. Kein Fehler, keine Warnung, falscher Datensatz. Verschicken Sie große IDs als Zeichenketten; die lange Fassung erklärt, warum jede andere Lösung ein Notbehelf ist.

Die strukturelle Lösung

Die Hälfte dieser Liste (die Punkte 2, 3, 4 und 5) kommt aus derselben Gewohnheit: JSON mit etwas anderem als einem Serialisierer zu erzeugen, meist mit Zeichenkettenverkettung oder einem Konsolen-Log.

# jeder dieser Fälle ist ein Bug, der auf die passende Eingabe wartet
body = '{"note": "' + note + '", "path": "' + path + '"}'

Ein Zeilenumbruch in note zerlegt es. Ein Backslash in path zerlegt es. Ein Anführungszeichen in einem von beiden zerlegt es, und wenn diese Eingabe von einem Benutzer kam, ist es eine Injection und kein Formatierungsproblem.

body = json.dumps({"note": note, "path": path}, allow_nan=False)

Der Serialisierer escapt, was escapt werden muss, setzt Anführungszeichen, wo welche hingehören, und weist zurück, was sich nicht darstellen lässt. Das ist keine Stilfrage. JSON von Hand zusammenzubauen heißt, die Escaping-Regeln aus Abschnitt 7 von RFC 8259 in jedem Zweig korrekt neu zu implementieren, und das tut niemand.

Wenn man Ihnen ein kaputtes Dokument statt eines kaputten Erzeugers übergibt, wendet JSON reparieren die obigen Korrekturen an und druckt eine Liste jeder vorgenommenen Änderung, damit Sie sehen können, ob es irgendwo geraten hat, bevor Sie der Ausgabe vertrauen.