Skip to main content
Developer Tools

Fehlerhaftes JSON debuggen: Ein Feldhandbuch

JSON-Parse-Fehler hinter wenig hilfreichen Meldungen aufdecken. Leitfaden zu den fünf häufigsten Fehlermodi und zur Eingrenzung auf das genaue fehlerhafte Byte.

By · · 6 min read

SyntaxError: Unexpected token } in JSON at position 4217. Diese Meldung hat man schon gesehen. Vielleicht gestern. Vielleicht vor zehn Minuten. Sie sagt, dass etwas falsch ist, ungefähr wo, und sonst nichts Nützliches.

Das ist ein praktischer Leitfaden zu dem kleinen Set von Fehlermodi, die fast alle JSON-Parse-Fehler in der Praxis verursachen, und wie man eine 10.000-Zeichen-Payload auf das genaue fehlerhafte Byte eingrenzt.

Die fünf Dinge, die meistens falsch gelaufen sind

1. Ein abschließendes Komma (Trailing Comma)

Der bei weitem häufigste Fehlermodus. Man hat eine Liste oder ein Objekt bearbeitet, das letzte Element gelöscht und vergessen, das Komma zu löschen:

{
  "name": "zeroutil",
  "category": "developer",
}

JavaScript akzeptiert das. JSON5 auch. Einfaches JSON nicht. Der Parser meldet den Fehler bei }, weil dort die Grammatik bricht - aber das eigentliche Problem ist das Komma davor.

Wie man es erkennt: nach ,\s*[}\]] mit einer Regex suchen. Unser Regex-Tester mit diesem Muster hebt jede Instanz hervor.

Wie man es verhindert: einen Formatter als Pre-Commit-Schritt verwenden. Von JSON.stringify generiertes JSON hat niemals abschließende Kommas. Trailing Commas sind ein Artefakt manueller Bearbeitung.

2. Nicht in Anführungszeichen stehende oder einfach in Anführungszeichen stehende Schlüssel

{
  name: "zeroutil",
  'category': "developer"
}

JavaScript-Objektliterale akzeptieren beides, JSON akzeptiert keines davon. Das passiert am häufigsten, wenn jemand JSON aus dem Gedächtnis tippt, ohne es in einen Parser zu laden.

Wie man es erkennt: unser JSON Formatter meldet die genaue Zeichenposition. In einer großen Datei findet grep -E "^\s+[a-zA-Z_]+\s*:" nicht in Anführungszeichen stehende Schlüssel.

3. Nicht maskierte Zeichen in Strings

Die Zeichen, die JSON erfordert, innerhalb von Strings zu maskieren, sind: ", \ und die Steuerzeichen (\n, \t, \r usw. - alles unter U+0020).

{
  "sql": "SELECT * FROM users WHERE id = \"42\""
}

ist gültig. Aber wenn man einen String einfügt, der einen nicht maskierten Zeilenumbruch enthält:

{
  "bio": "Zeile eins
Zeile zwei"
}

sieht der Parser den Zeilenumbruch als Terminator und stürzt ab. Das Verwirrende ist, dass die Fehlermeldung normalerweise auf das Zeichen nach dem Zeilenumbruch zeigt, nicht auf den Zeilenumbruch selbst.

Wie man es erkennt: wenn die Fehlerposition keinen offensichtlichen Sinn ergibt, wenn man das Zeichen an dieser Position betrachtet, die Zeile darüber prüfen - ein nicht maskiertes Steuerzeichen hat einen String vorzeitig beendet.

Wie man sicheres JSON generiert: immer JSON.stringify oder das Äquivalent der eigenen Sprache verwenden, um die Ausgabe zu erstellen. Keine Strings in eine JSON-Vorlage konkatenieren; so landen nicht maskierte Anführungszeichen in der Payload.

4. NaN, Infinity, undefined

JavaScripts JSON.stringify produziert null für NaN und Infinity und lässt undefined-Werte vollständig weg. Wenn diese Literale in einer “JSON”-Payload vorkommen, hat sie jemand mit einem nicht-standardkonformen Serialisierer erzeugt - das json-Modul von Python mit allow_nan=True emittiert beispielsweise literales NaN, das andere Sprach-Parser ablehnen werden.

{
  "accuracy": NaN,
  "max_rate": Infinity
}

Gültiges JavaScript, gültiges JSON5, ungültiges JSON.

Wie man es erkennt: nach \bNaN\b|\bInfinity\b|\bundefined\b suchen.

Wie man die Quelle behebt: in Python json.dumps(obj, allow_nan=False) verwenden, um beim Serialisieren einen Parse-Fehler zu bekommen statt Müll zu emittieren. In JavaScript entweder vor dem Serialisieren filtern oder eine replacer-Funktion an JSON.stringify übergeben, die diese Werte konvertiert.

5. BOM, versehentliche Leerzeichen und Kodierungsprobleme

Einige Editoren und Exporte fügen am Anfang der Datei eine Byte-Order-Markierung (U+FEFF) hinzu. Die meisten JSON-Parser scheitern daran - das erste Byte soll { oder [ sein, und sie akzeptieren keine UTF-8-BOM als führenden Leeraum.

<BOM>{"key": "value"}

Sieht identisch mit einer gültigen JSON-Datei aus, bis man die Bytes prüft.

Wie man es erkennt: file unter Unix meldet “UTF-8 Unicode (with BOM) text.” head -c 3 file.json | xxd zeigt die führenden Bytes. ef bb bf ist die BOM.

Wie man es behebt: entfernen. sed '1s/^\xEF\xBB\xBF//' oder die Datei als “UTF-8 ohne BOM” im Editor speichern.

Strategie 1: die Position einschranken

Die Fehlermeldung des Parsers gibt eine Zeichenposition an. Diese nutzen:

try {
  JSON.parse(payload);
} catch (err) {
  const pos = Number(err.message.match(/position (\d+)/)?.[1]);
  console.log(payload.slice(Math.max(0, pos - 80), pos + 80));
  console.log(' '.repeat(Math.min(80, pos)) + '^');
}

Das gibt 80 Zeichen Kontext um die fehlschlagende Position herum aus, mit einem Dach, das darauf zeigt. In der Praxis liegt das eigentliche Problem normalerweise 1-20 Zeichen links der gemeldeten Position - die Grammatik hat gehalten, bis sie nicht mehr konnte.

Strategie 2: eine riesige Payload binär halbieren

Wenn die Payload zu groß ist, um sie zu überfliegen, ist der schnellste Weg zum fehlerhaften Byte eine Halbierungssuche:

function findBreakingSlice(text) {
  let lo = 0, hi = text.length;
  while (hi - lo > 1) {
    const mid = Math.floor((lo + hi) / 2);
    try {
      JSON.parse(text.slice(0, mid) + '}]'.repeat(10));
      lo = mid;
    } catch {
      hi = mid;
    }
  }
  return hi;
}

Das ist nicht perfekt (das abschließende }] ist eine billige Möglichkeit, gültige Struktur zu erzwingen), aber es schränkt eine 10-KB-Datei in etwa 10 Schritten auf ein 10-Byte-Fenster ein.

Strategie 3: gegen eine bekannte gute Version vergleichen

Wenn dasselbe JSON gestern geladen hat und heute fehlschlägt, ist das Schnellste ein Vergleich mit der vorherigen Version. Unser Diff Checker läuft vollständig im Browser, was wichtig ist, wenn die Payload irgendetwas Sensibles enthält - man möchte keine Produktionsantwort in einen zufälligen Diff-Dienst einfügen.

Der Diff ist bei formatiertem JSON am nützlichsten. Beide Versionen zuerst durch den JSON Formatter laufen lassen, damit strukturelle Änderungen sauber erscheinen und nicht in einer riesigen Zeile versteckt sind.

Strategie 4: den Produzenten fragen, was er getan hat

Wenn eine Payload, die von einer API kommt, fehlerhaft ist, liegt der eigentliche Fehler normalerweise auf der Produzentenseite. Ein spezifisches Muster: der Antwortkörper wurde mitten im Schreiben abgeschnitten, weil eine Verbindung geschlossen wurde, ein Buffer zum falschen Zeitpunkt geleert wurde oder ein Timeout während der Serialisierung aufgetreten ist. Der empfangene Text endet mitten in einem String oder einem Objekt.

Wenn das letzte Zeichen nicht } oder ] ist, ist es wahrscheinlich eine Kurzung, kein fehlerhafter Wert. Der Fix liegt auf dem Server, nicht in der Parser-Konfiguration.

Eine Checkliste für “dieses JSON ist kaputt und ich habe fünf Minuten”

  1. In den Formatter einfügen. Wenn er sich beschwert, liegt die Fehlerposition normalerweise innerhalb von 20 Zeichen des eigentlichen Problems.
  2. Nach ,\s*[}\]] suchen. Trailing Commas sind Ursache Nummer 1.
  3. Das letzte Zeichen prüfen. Wenn es nicht } oder ] ist, wurde der Stream abgeschnitten.
  4. Die ersten drei Bytes prüfen. BOM in Hex ist ef bb bf.
  5. Nach NaN, Infinity, nicht maskierten Anführungszeichen ([^\\]") suchen.

Fünf Schritte, die meisten Fehler abgefangen. Der Rest der Fehler - tief verschachtelte strukturelle Probleme, wirklich seltsame Kodierungsprobleme - ist selten genug, dass sie die volle 30-minütige Untersuchung verdienen, wenn sie auftreten.

Die Meta-Lektion

JSON-Parsing ist seit fünfzehn Jahren stabil und langweilig. Wenn JSON fehlerhaft ist, hat es fast sicher ein Mensch getippt, ein kaputtes Serialisierer produziert oder ein Netzwerkproblem es abgeschnitten. Keines davon erfordert ein tiefes Verständnis der Spezifikation - es erfordert, die häufigen Fehlermodi auswendig zu kennen und Tools für die binäre Sucharbeit bereit zu haben.

Die meisten Tools in der Wildnis, die behaupten, fehlerhaftes JSON zu “reparieren”, indem sie es tolerant neu interpretieren, tun einem keinen Gefallen. Die Quelle reparieren, den Parser nicht uberkleben.

In diesem Artikel erwähnte Tools

  • JSON Formatter - Format, validate and minify JSON with syntax highlighting.
  • Regex Tester - Test regular expressions with live highlighting, matches and capture groups.
  • Diff Checker - Compare code or text with line-by-line diff and unified output.

Ähnliche Artikel