---
id: 61f2009278a5f7366f79395da59447cf
author: lemonmint
title: Analýza změn chování encoding/json/v2 v Go 1.25 a vrstvy kompatibility s v1
description: Porovnáváme porovnávání názvů polí bez ohledu na velikost písmen, změnu významu omitempy/omitzero, serializaci nil slices a map a související možnosti u encoding/json/v2, který byl zaveden jako experimentální funkce Go 1.25 (goexperiment.jsonv2).
language: cs
date: 2025-06-20T05:38:20.100520237Z
path: /blog/posts/go-1-25-encoding-json-v1-vs-v2-z74a36b08
category: release
---

Balíček `encoding/json` v2 v Go je novou implementací, jejímž cílem je odstranit několik nedostatků stávající verze v1 (nedostatečnou konzistenci, překvapivé chování a problémy s výkonem). Jde o experimentální funkci, která se aktivuje pomocí build tagu `goexperiment.jsonv2`.

Nejdůležitější je, že **když je v2 aktivní, v1 funguje jako kompatibilní vrstva, která na implementaci v2 emuluje chování v1**. Děje se tak prostřednictvím funkce `DefaultOptionsV1()` v souboru `v2_v1.go`. To znamená, že v2 nabízí možnosti, které dokážou chování v1 zcela reprodukovat, a zároveň zavádí přísnější a předvídatelnější nové výchozí chování.

Hlavní cíle v2 jsou následující:

1. **Zvýšení správnosti a předvídatelnosti**: Výchozím nastavením jsou uplatňována přísnější pravidla (např. rozlišování velikosti písmen, zákaz duplicitních klíčů), čímž se omezuje neočekávané chování.
2. **Zlepšení výkonu**: Enginy pro parsování a kódování byly přepracovány, aby byla zvýšena efektivita.
3. **Větší flexibilita a kontrola**: Byl zaveden podrobný systém možností, který vývojářům umožňuje jemně řídit způsob zpracování JSON.

---

## Hlavní rozdíly ve významu a chování

Rozdíly mezi v1 a v2 jsou níže uspořádány podle položek se zaměřením na soubor `v2_test.go`.

### 1. Porovnávání názvů polí (rozlišování velikosti písmen)

- **Chování v1**: Při unmarshalingu členů JSON objektu do polí struktury v Go se **velikost písmen ignoruje (case-insensitive)**. Hodnoty `"FirstName"` i `"firstname"` se namapují na pole `FirstName`.
- **Chování v2**: Výchozím nastavením se **rozlišuje velikost písmen (case-sensitive)** a namapují se pouze pole, jejichž název přesně odpovídá.
- **Důvod změny**: Porovnávání bez ohledu na velikost písmen může způsobovat neočekávané chování a při zpracování neodpovídajících polí snižuje výkon. v2 proto jako výchozí přijala jasnější a předvídatelnější chování.
- **Související možnosti**: V v2 lze tag `json:"...,case:ignore"` použít k explicitnímu zapnutí ignorování velikosti písmen pro jednotlivá pole, nebo lze globálně aplikovat možnost `json.MatchCaseInsensitiveNames(true)`.

### 2. Změna významu volby `omitempty`

- **Chování v1**: Pole se vynechá na základě „prázdného stavu“ hodnoty v Go. „Prázdným stavem“ se zde rozumí `false`, `0`, `nil` ukazatel či rozhraní a pole, řetězec, slice nebo mapa s délkou 0.
- **Chování v2**: Pole se vynechá na základě „prázdného stavu“ zakódované JSON hodnoty. Vynechá se tedy tehdy, je-li zakódována jako `null`, `""`, `{}` nebo `[]`.
- **Důvod změny**: Definice v1 závisí na typovém systému Go. v2 vychází z typového systému JSON, a poskytuje tak konzistentnější chování. Například v v1 se hodnota `false` typu `bool` vynechá, zatímco v v2 se nevynechá, protože `false` je v JSONu neprázdná hodnota. v v2 byla přidána možnost `omitzero`, která nahrazuje chování v1, kdy se `omitempty` uplatňovalo na `0` nebo `false`.
- **Související možnosti**: Pokud chcete v v2 chování shodné s v1, použijte možnost `json.OmitEmptyWithLegacyDefinition(true)`.

### 3. Změna chování volby `string`

- **Chování v1**: Použije se u polí číselného, logického a řetězcového typu. Příslušná hodnota se znovu zakóduje uvnitř JSON řetězce (např. `int(42)` -> `"42"`). Na hodnoty uvnitř složených typů (slice, mapa apod.) se nepoužije rekurzivně.
- **Chování v2**: Použije se **pouze u číselných typů** a aplikuje se rekurzivně. Čísla uvnitř slicu typu `[]int` jsou tedy také zakódována jako JSON řetězce.
- **Důvod změny**: Hlavním účelem volby `string` je zabránit ztrátě přesnosti 64bitových celých čísel tím, že se čísla vyjádří jako řetězce. Chování v1 bylo omezené a nekonzistentní. v2 se soustředí na tento klíčový účel a rozšiřuje chování rekurzivně, čímž je užitečnější.
- **Související možnosti**: Chování v1 lze napodobit pomocí možnosti `json.StringifyWithLegacySemantics(true)`.

### 4. Marshaling hodnot `nil` pro slice a mapy

- **Chování v1**: `nil` slice a `nil` mapa se marshalují jako `null`.
- **Chování v2**: Výchozím nastavením se `nil` slice marshaluje jako `[]` (prázdné pole) a `nil` mapa jako `{}` (prázdný objekt).
- **Důvod změny**: `nil` je implementační detail jazyka Go a není vhodné jej vystavovat ve formátu JSON, který nezávisí na jazyce. Pro vyjádření prázdné kolekce jsou běžnější `[]` a `{}`.
- **Související možnosti**: V v2 lze pomocí možností `json.FormatNilSliceAsNull(true)` nebo `json.FormatNilMapAsNull(true)` dosáhnout marshalingu jako `null`, jako v v1.

### 5. Unmarshaling polí

- **Chování v1**: Při unmarshalingu do pole Go (`[N]T`) nevznikne chyba, i když se délka JSON pole liší od délky pole Go. Pokud je JSON pole kratší, zbylé prvky se vyplní nulovými hodnotami, a pokud je delší, přebytek se zahodí.
- **Chování v2**: Délka JSON pole se musí **přesně shodovat** s délkou pole v Go. V opačném případě dojde k chybě.
- **Důvod změny**: V Go má délka pole s pevnou velikostí často zásadní význam. Chování v1 může tiše způsobit ztrátu dat. v2 přísnějším pravidlem zvyšuje správnost.
- **Související možnosti**: Chování v1 lze napodobit pomocí možnosti `json.UnmarshalArrayFromAnyLength(true)`.

### 6. Zpracování `time.Duration`

- **Chování v1**: `time.Duration` je interně považován za `int64` a kóduje se jako číslo v JSON v nanosekundách.
- **Chování v2**: Kóduje se jako JSON řetězec ve formátu jako `"1h2m3s"` pomocí metody `time.Duration.String()`.
- **Důvod změny**: Číslo v nanosekundách je hůře čitelné a standardní řetězcová reprezentace `time.Duration` je užitečnější.
- **Související možnosti**: Chování v1 lze použít pomocí tagu `json:",format:nano"` nebo možnosti `json.FormatTimeWithLegacySemantics(true)`.

### 7. Zpracování neplatného UTF-8

- **Chování v1**: Při marshalingu/unmarshalingu se neplatné bajty UTF-8 v řetězcích **tiše nahradí** Unicode náhradním znakem (`\uFFFD`).
- **Chování v2**: Výchozím nastavením se při narazení na neplatné UTF-8 **vrátí chyba**.
- **Důvod změny**: Cílem je zabránit tichému poškození dat a dodržet přísnější standard JSON (RFC 7493).
- **Související možnosti**: Chování v1 lze napodobit pomocí možnosti `jsontext.AllowInvalidUTF8(true)`.

### 8. Zpracování duplicitních názvů členů objektu

- **Chování v1**: V JSON objektu je povoleno, aby se člen se stejným názvem vyskytoval vícekrát. Použije se hodnota, která se objeví jako poslední.
- **Chování v2**: Výchozím nastavením se při existenci duplicitního názvu člena **vrátí chyba**.
- **Důvod změny**: Standard RFC 8259 nedefinuje chování u duplicitních názvů, takže se implementace mohou lišit. Může to být příčinou bezpečnostních zranitelností. v2 tyto duplicity explicitně odmítá, čímž zvyšuje správnost a bezpečnost.
- **Související možnosti**: Chování v1 lze napodobit pomocí možnosti `jsontext.AllowDuplicateNames(true)`.

---

## Rozdíly v implementaci a architektuře

- **v1**: Silně závisí na `decodeState` v souboru `decode.go` a na ručně napsaném stavovém automatu (state machine) v souboru `scanner.go`. Jde o monolitickou strukturu, v níž jsou parsovací logika a sémantická analýza úzce provázány.
- **v2**: Architektura je více modulární.
  - **`encoding/json/jsontext`**: Poskytuje nízkoúrovňový vysoce výkonný tokenizér JSON (`Decoder`) a enkodér (`Encoder`). Tento balíček se zaměřuje výhradně na syntaktickou stránku JSON.
  - **`encoding/json/v2`**: Na základě `jsontext` zajišťuje sémantickou transformaci mezi typy Go a hodnotami JSON.
  - Toto oddělení odděluje syntaktickou analýzu od sémantické analýzy, což zvyšuje přehlednost kódu a výkon.

---

## Nová API a funkce v2

v2 nabízí velmi flexibilní řízení prostřednictvím systému `json.Options`.

- **`json.Options`**: Množina možností, které mění chování při marshalingu/unmarshalingu.
- **`json.JoinOptions(...)`**: Spojí více možností do jedné.
- **`WithMarshalers` / `WithUnmarshalers`**: Výkonná funkce, která umožňuje vložit logiku serializace/deserializace pro konkrétní typ, aniž by typ musel implementovat rozhraní `Marshaler`/`Unmarshaler`. Je užitečná zejména při práci s typy z externích balíčků.
- **Nové možnosti**: `RejectUnknownMembers`, `Deterministic(false)`, `FormatNilSliceAsNull` a další umožňují řídit chování, které ve v1 nebylo možné.

---

## Závěr

`encoding/json` v2 je moderní implementací, která na základě zkušeností s v1 výrazně zlepšuje **správnost, výkon a flexibilitu**. Výchozí chování je přísnější, ale díky propracovanému systému `Options` lze plně podpořit všechna chování v1, takže lze zachovat kompatibilitu se stávajícím kódem a postupně přijímat výhody v2.

- **Pro nové projekty** se doporučuje používat v2 jako výchozí volbu.
- **Stávající projekty** mohou buď nadále používat `jsonv1`, nebo migrovat na `jsonv2` a postupně zavádět přísnější chování v2 pomocí `DefaultOptionsV1()`.
