FAQ zu Syntax und Formaten: Kommentare, nachfolgende Kommas und Präzision
Fazit: Ob Kommentare erlaubt sind, warum ein nachfolgendes Komma einen Fehler auslöst, ob Schlüssel Anführungszeichen brauchen, warum 9007199254740993 sich ändert – zwölf Schlussfolgerungen, jede gegen RFC 8259 geprüft.

Kurz gesagt: Schreiben Sie reines RFC-8259-JSON – doppelte Anführungszeichen, keine Kommentare, keine nachfolgenden Kommas, UTF-8 ohne BOM – für alles, was eine Prozessgrenze überschreitet; verwenden Sie JSONC oder JSON5 für Konfiguration, die Kommentare braucht, und konvertieren Sie zur Laufzeit zurück; und serialisieren Sie Ganzzahlen oberhalb von 2^53-1 auf dem Server als Strings.
Was diese Fragen gemeinsam haben: Sie sehen aus, als „sollten sie funktionieren“, aber die Spezifikation ist tatsächlich sehr streng.
1. Was als gültiges JSON gilt
Was ist der Unterschied zwischen JSON, JSON5 und JSONC?
Standard-JSON erkennt nur doppelt quotierte Strings und erlaubt weder Kommentare noch nachfolgende Kommas. JSON5 und JSONC fügen beide Funktionen hinzu:
| Funktion | JSON (RFC 8259) | JSONC | JSON5 |
|---|---|---|---|
| Doppelt quotierte Strings | Unterstützt | Unterstützt | Unterstützt |
| Einfach quotierte Strings | Nicht unterstützt | Nicht unterstützt | Unterstützt |
| Kommentare | Nicht unterstützt | Unterstützt | Unterstützt |
| Nachfolgende Kommas | Nicht unterstützt | Nicht unterstützt | Unterstützt |
| Unquotierte Schlüssel | Nicht unterstützt | Nicht unterstützt | Unterstützt |
| Hexadezimale Zahlen | Nicht unterstützt | Nicht unterstützt | Unterstützt |
NaN / Infinity |
Nicht unterstützt | Nicht unterstützt | Unterstützt |
| Dateiendung | .json |
.jsonc |
.json5 |
Das Auswahlprinzip ist einfach: über Prozessgrenzen hinweg immer Standard-JSON verwenden, während Konfigurationsdateien JSONC oder JSON5 verwenden dürfen, je nachdem, was Ihr Editor unterstützt.
tsconfig.json und .vscode/settings.json können Kommentare enthalten, weil der Editor sie als JSONC parst; sie einem strikten
JSON.parse zu geben, erzeugt trotzdem einen Fehler.
Kann JSON Kommentare enthalten?
Nein. Die Grammatik in RFC 8259 kennt keine Kommentarproduktion, jedes // oder /* */ lässt das Parsen also scheitern.
Wollen Sie Erklärungen in der Konfiguration behalten, gibt es drei Alternativen:
{
"_comment": "以下超时单位是毫秒",
"timeout": 3000,
"retry": {
"$doc": "重试次数,0 表示不重试",
"count": 3
}
}
- Verwenden Sie konventionelle Felder wie
_comment, die der Leser einfach ignoriert; - Verwenden Sie
$-präfixierte Felder, um die Chance auf Kollision mit Geschäftsfeldern zu verringern; - Wechseln Sie für Konfiguration einfach zu JSONC oder YAML und konvertieren Sie zur Laufzeit zu Standard-JSON.
Warum verursacht ein nachfolgendes Komma einen Fehler?
Weil in der Grammatik sowohl Arrays als auch Objekte „Element + Trennzeichen“-Strukturen sind, und nach dem letzten Element ist kein Komma erlaubt.
{
"a": 1,
"b": 2,
}
Das obige Fragment hat ein überzähliges Komma vor }, und der Fehler wird je nach Parser-Implementierung meist am Komma oder an der schließenden Klammer gemeldet.
Das ist auch die leichteste Falle bei der Migration von JavaScript – JS-Objektliterale erlauben nachfolgende Kommas, JSON nicht.
Sind einfache Anführungszeichen erlaubt?
Nein. Strings müssen doppelte Anführungszeichen verwenden; 'text' ist ein Syntaxfehler.
Wurden die Daten aus Pythons repr(), einem Shell-Skript oder handgeschriebener Konfiguration kopiert, konvertieren Sie sie zuerst mit einem Werkzeug,
statt zu hoffen, dass der Parser nachsichtig ist.
Können Schlüssel unquotiert sein?
Nein. Objektmitgliedsnamen müssen in doppelte Anführungszeichen gefasste Strings sein.
{ name: "json" }
Das ist ein JavaScript-Objektliteral, kein JSON. Anführungszeichen hinzuzufügen macht es gültig:
{ "name": "json" }
Kann die oberste Ebene ein Array oder ein String sein?
Ja. RFC 8259 erlaubt jeden JSON-Wert auf oberster Ebene: Objekte, Arrays, Strings, Zahlen, true, false und null sind alle gültig.
In der technischen Praxis empfiehlt es sich jedoch, auf oberster Ebene nur Objekte oder Arrays zu verwenden, aus zwei Gründen:
- Kompatibilität: Ältere Parser (aus der RFC-4627-Ära) verlangen, dass die oberste Ebene ein Objekt oder ein Array ist;
- Sicherheit: Ein Array auf oberster Ebene trug historisch ein JSON-Hijacking-Risiko. Moderne Browser haben es behoben, aber ältere Systeme nehmen die Konvention weiterhin an.
"hello" ist also gültiges JSON, und ["a", "b"] auch, aber eine API, die {"data": [...]} zurückgibt, ist sicherer.
2. Kodierung und Strings
Bricht ein BOM am Dateianfang das Parsen?
Ja. RFC 8259 verlangt ausdrücklich, dass JSON-Text als UTF-8 übertragen wird und kein Byte Order Mark (BOM) hinzugefügt werden darf.
Das BOM sind die drei Bytes EF BB BF ganz am Dateianfang. Es ist völlig unsichtbar, belegt aber die erste Position des Strings,
der Parser schließt also, dass „das erste Zeichen nicht { ist“, und scheitert – und die gemeldete Zeile und Spalte sind meist Zeile 1, Spalte 1, was verwirrend aussieht.
Windows Notepads „Als UTF-8 speichern“ enthält standardmäßig ein BOM. Zum Prüfen und Entfernen:
# Check: output starting with efbbbf means a BOM is present
head -c 3 data.json | xxd
# Remove: skip the first 3 bytes with tail
tail -c +4 data.json > clean.json
Das JSON.parse moderner JavaScript-Umgebungen toleriert ein BOM (V8 überspringt es intern), aber strenge Implementierungen in Java, Go und Python tun das nicht,
was ein häufiger Grund ist, warum „dieselbe Datei in verschiedenen Sprachen unterschiedliche Ergebnisse liefert“.
Müssen chinesische Zeichen in \uXXXX umgewandelt werden?
Nein. RFC 8259 verlangt, dass JSON-Text UTF-8 ist, Chinesisch darf also direkt erscheinen; {"name": "张三"} ist vollkommen gültig.
\uXXXX ist lediglich eine äquivalente alternative Schreibweise, verwendet für Kanäle, die das Zeichen nicht direkt darstellen können. Die Faustregel:
- HTTP-Request-Bodies, Dateien, Datenbanken: Chinesisch direkt schreiben, spart 3 Bytes pro Zeichen;
- HTTP-Header, URL-Parameter, Einbetten in einen anderen String: Escapes verwenden, um Kodierungsprobleme zu vermeiden.
Die einzigen Zeichen, die zwingend escaped werden müssen, sind Steuerzeichen (U+0000 bis U+001F). Ein Zeilenumbruch muss etwa als \n geschrieben werden;
Sie können keinen literalen Zeilenumbruch in einen String setzen.
Können Emojis direkt in einen String?
Ja, aber denken Sie daran, dass sie zwei UTF-16-Codeeinheiten belegen.
Emojis und viele seltene Zeichen (etwa 𠮷) liegen außerhalb der BMP, in JavaScript gilt also "😀".length === 2.
Die Validierung nach Zeichenzahl ist daher leicht falsch:
const text = "😀";
console.log(text.length); // 2, number of UTF-16 code units
console.log([...text].length); // 1, number of code points
console.log(Buffer.byteLength(text)); // 4, number of UTF-8 bytes
Wird ein Nickname-Limit mit .length berechnet, verbraucht ein Emoji zwei Slots; zählt das Backend Bytes, sind es vier.
Formulieren Sie beim Abgleichen der Definitionen klar, ob Sie Codepoints, Codeeinheiten oder Bytes meinen.
3. Zahlen und Sonderwerte
Warum wurde die große Ganzzahl 9007199254740993 zu 9007199254740992?
Weil JavaScripts JSON.parse Zahlen in IEEE-754-Double-Floats umwandelt und die größte sichere Ganzzahl 2^53-1 ist.
Number.MAX_SAFE_INTEGER === 9007199254740991 // true
9007199254740993 // actually yields 9007199254740992
Jede Zahl oberhalb von 2^53-1 kann in ihren Endziffern gerundet werden. Typische Opfer sind 19-stellige Nutzer-IDs aus Snowflake-Algorithmen, Bestellnummern und Nanosekunden-Zeitstempel.
Drei Behebungen, nach Präferenz geordnet:
- Die ID auf dem Server als String serialisieren:
"id": "9007199254740993", was das Präzisionsproblem an der Wurzel beseitigt; - Eine verlustfreie Parsing-Bibliothek verwenden: Ersatz für
JSON.parsekann große Ganzzahlen wörtlich bewahren, etwajson-bigint; - Mit einem regulären Ausdruck nur zur Anzeige extrahieren: nicht das ganze Dokument parsen, sondern das Zielfeld als Text treffen.
Beachten Sie, dass JSON.stringify das symmetrische Problem hat: Eine Zahl außerhalb des sicheren Ganzzahlbereichs hält nach der Serialisierung womöglich nicht mehr ihren ursprünglichen Wert.
Können NaN und Infinity in JSON?
Nein. Die JSON-Zahlenproduktion erlaubt nur Dezimalnotation, und NaN, Infinity und -Infinity sind keine gültigen JSON-Werte.
JavaScripts Ansatz ist „stille Degradierung“:
JSON.stringify({ value: NaN }); // '{"value":null}'
JSON.stringify({ value: Infinity }); // '{"value":null}'
Kein Fehler, NaN wird einfach zu null. Das ist die klassische Szene, in der „das Frontend NaN gesendet hat und das Backend null empfangen hat“.
Müssen Sie solche Semantik vermitteln, drücken Sie sie ausdrücklich mit Strings oder vereinbarten Sentinel-Werten aus:
{ "ratio": null, "ratioState": "not-a-number" }
Können Zahlen als 1e3 oder 0.5 geschrieben werden?
Ja. JSON-Zahlen unterstützen Dezimalstellen und Exponenten, 1e3, 1E3, 0.5 und -0.5 sind also alle gültig.
Es gibt aber Einschränkungen:
- Sie dürfen nicht mit
.beginnen oder enden:.5und5.sind beide ungültig, schreiben Sie also0.5und5; - Keine führenden Nullen:
01ist ungültig; - Kein Hexadezimal:
0x1Fist ungültig, das ist eine JSON5-Erweiterung; -0ist gültig, aberJSON.stringify(-0)gibt0aus und verliert das Vorzeichen.
4. Objektsemantik
Was passiert bei doppelten Schlüsseln?
Das Verhalten ist implementierungsdefiniert; RFC 8259 sagt nur, Schlüsselnamen „sollten“ eindeutig sein, und legt nicht fest, was bei Duplikaten passiert.
In der Praxis nimmt die überwiegende Mehrheit der Implementierungen den letzten Wert:
JSON.parse('{"a":1,"a":2}') // { a: 2 }
Aber Sie können sich nicht darauf verlassen. Manche Java-Bibliotheken, Gos encoding/json und Pythons json unterscheiden sich zwischen Versionen:
Manche nehmen den ersten Wert, manche werfen einen Fehler, manche nehmen den letzten. Doppelte Schlüssel als Fehler zu behandeln ist sprachübergreifend der sicherste Ansatz.
Doppelte Schlüssel finden Sie nicht mit dem Auge – in minifiziertem JSON kann derselbe Schlüssel Tausende Zeichen entfernt sein. Verwenden Sie ein Skript:
# Extract keys that appear more than once at the same level, as a rough check
jq -r 'paths(scalars) | join(".")' data.json | sort | uniq -d
Was wird aus undefined?
Das hängt ab, und das ist die fehleranfälligste Stelle:
JSON.stringify({ a: undefined }); // '{}' the key is dropped entirely
JSON.stringify({ a: undefined, b: 1 }); // '{"b":1}'
JSON.stringify([undefined]); // '[null]' becomes null inside arrays
JSON.stringify({ a: function () {} }); // '{}' functions are dropped too
Dasselbe undefined bedeutet innerhalb eines Objekts „das Feld verschwindet“ und innerhalb eines Arrays „wird zu null“.
Nimmt das Backend unterschiedliche Zweige für „Feld fehlt“ und „Feld ist null“, bekommen Sie hier eine Inkonsistenz.
Müssen Sie „kein Wert“ ausdrücklich ausdrücken, verwenden Sie null statt undefined.
Was passiert nach dem Parsen von {"__proto__": {...}}?
JSON.parse selbst ist sicher: __proto__ wird als gewöhnliche eigene Eigenschaft behandelt und ändert den Prototyp nicht.
Gefährlich ist die Merge-Operation nach dem Parsen:
const patch = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign({}, patch); // some implementations pollute the prototype
Der richtige Ansatz ist, eigene Eigenschaften zu durchlaufen und __proto__, constructor und prototype zu überspringen.
Details stehen in der FAQ zu Performance und Sicherheit.
5. Kurzreferenz
| Syntax | Gültig | Hinweise |
|---|---|---|
{"a": 1,} |
Nein | Nachfolgendes Komma |
{'a': 1} |
Nein | Einfache Anführungszeichen |
{a: 1} |
Nein | Schlüssel ohne Anführungszeichen |
{"a": 1} // comment |
Nein | Kommentare |
"hello" |
Ja | Ein String auf oberster Ebene ist gültig, aber in APIs selten verwendet |
{"a": 1e3} |
Ja | Exponenten werden unterstützt |
{"a": 01} |
Nein | Führende Null |
{"a": NaN} |
Nein | Ungültiges Literal; stringify macht daraus null |
\uFEFF{"a":1} |
Nein | Ein BOM ist kein Teil von JSON-Text |
{"a":1,"a":2} |
Umstritten | Implementierungen nehmen den letzten Wert, aber verlassen Sie sich nicht darauf |