Ratgeber · JSON 2026
JSON und UTF-8: Encoding-Edge-Cases die Pipelines killen
JSON ist immer UTF-8, ohne BOM. Aber Windows-Tools schreiben gerne BOMs rein, und Python 2 hatte ein eigenes Problem mit Surrogate-Pairs.
Von Mateusz Viola
Betreiber & redaktionelle Verantwortung json-formatieren.de
Veröffentlicht
Aktualisiert:
JSON ist immer UTF-8
RFC 8259 schreibt es explizit: "JSON text exchanged between systems that are not part of a closed ecosystem MUST be encoded using UTF-8". Andere Encodings (UTF-16, UTF-32) sind formal noch erlaubt, in der Praxis aber nicht relevant. Wer heute JSON schreibt oder parst, geht immer von UTF-8 aus.
BOM ist verboten
Eine UTF-8-BOM (Byte Order Mark) ist die Byte-Folge EF BB BF am Datei-Anfang. RFC 8259 verbietet sie in JSON explizit. Wer trotzdem eine BOM einfügt, bekommt von JSON.parse:
SyntaxError: Unexpected token in JSON at position 0
Häufige Verursacher:
- Windows-Notepad bei "Save as UTF-8" (klassisch, alte Versionen)
- PowerShell mit
Out-File -Encoding utf8(älter als PS 7) - Excel beim CSV-zu-JSON-Konvertieren
Lösung: Editor auf "UTF-8 ohne BOM" stellen. In PowerShell 7+: Out-File -Encoding utf8NoBOM. In VS Code: Statusbar rechts unten klickbar.
Surrogate-Pairs für Codepoints > U+FFFF
JSON-Strings sind als Unicode-Codepoint-Sequenz spezifiziert. Codepoints bis U+FFFF (Basic Multilingual Plane) können direkt als \uXXXX-Sequenz escaped werden. Für höhere Codepoints (typisch Emojis) braucht es Surrogate-Pairs:
| Zeichen | Codepoint | JSON-Escape |
|---|---|---|
| ä | U+00E4 | \u00e4 |
| € | U+20AC | \u20ac |
| 😀 | U+1F600 | \uD83D\uDE00 |
| 𝄞 | U+1D11E | \uD834\uDD1E |
Surrogate-Pairs sind ein UTF-16-Konstrukt, das sich in JSON-Escape-Sequenzen niederschlägt. JSON.stringify in V8 nutzt heute lieber direkte UTF-8-Bytes statt Escape-Sequences - kürzer und genauso valide.
Lone Surrogates: der dunkle Edge-Case
JavaScript-Strings sind UTF-16, und es ist technisch möglich, einen "lone surrogate" zu haben - also nur die obere oder untere Hälfte eines Surrogate-Pairs. JSON.stringify in modernen V8-Versionen ersetzt solche Lone Surrogates durch das Replacement-Character U+FFFD (). Ältere Implementierungen geben sie unverändert raus - und der resultierende JSON ist dann nicht valides UTF-8.
Pflicht-Test in API-Pipelines: prüfen ob alle eingehenden Strings valide UTF-8 produzieren würden. Library: well-formed für JS.
Encoding-Pflichten pro Sprache
| Sprache | Default | Anmerkung |
|---|---|---|
| JavaScript | UTF-8 (kein BOM) | JSON.stringify garantiert es |
| Python 3 | UTF-8 | json.dumps(ensure_ascii=False) für lesbare Output |
| Go | UTF-8 | encoding/json garantiert es |
| Java | abhängig vom Writer | OutputStreamWriter mit "UTF-8" explizit setzen |
| PowerShell 5 | UTF-8 mit BOM (!) | Out-File -Encoding utf8 schreibt BOM. PS7+: utf8NoBOM nutzen |
HTTP-Header und Encoding
Im Content-Type-Header brauchst du kein charset-Suffix:
Content-Type: application/json // korrekt
Content-Type: application/json; charset=utf-8 // redundant, aber harmlos
RFC 8259 sagt: JSON-Mediatype ist per Definition UTF-8. Der charset-Parameter ist nicht definiert und wird von Parsern ignoriert. Schadet aber auch nicht - viele Frameworks (Spring, Express) setzen ihn aus Habitus.
Praxis-Empfehlung
- JSON nie als UTF-16 oder UTF-32 ausgeben - nutze immer UTF-8.
- BOM strippen vor Parsing wenn du Dateien aus Windows-Systemen liest.
- Bei API-Boundary: incoming Payloads auf valides UTF-8 prüfen, vor Schema-Validation.
- JSON.stringify als Default vertrauen - es macht das Richtige seit Jahren.