Porovnanie Go 1.25 encoding/json v1 vs v2
Balík encoding/json
v2 jazyka Go predstavuje novú implementáciu zameranú na zlepšenie viacerých nedostatkov existujúcej verzie v1 (nedostatočná konzistentnosť, prekvapujúce správanie, problémy s výkonom). Ide o experimentálnu funkciu, ktorá sa aktivuje prostredníctvom build tagu goexperiment.jsonv2
.
Najdôležitejšie je, že keď je v2 aktívne, v1 funguje ako kompatibilná vrstva, ktorá emuluje správanie v1 nad implementáciou v2. Toto sa dosahuje prostredníctvom funkcie DefaultOptionsV1()
v súbore v2_v1.go
. To znamená, že v2 poskytuje možnosti na dokonalú reprodukciu správania v1 a zároveň zavádza nové, prísnejšie a predvídateľnejšie predvolené správanie.
Hlavné ciele v2 sú nasledovné:
- Zlepšenie presnosti a predvídateľnosti: Predvolene sa aplikujú prísnejšie pravidlá (napr. rozlišovanie veľkých a malých písmen, zákaz duplicitných kľúčov), čím sa znižuje výskyt neočakávaného správania.
- Zlepšenie výkonu: Prepracovaním parsovacieho a kódovacieho enginu sa zvýšila efektivita.
- Rozšírenie flexibility a kontroly: Zavedenie podrobného systému Options umožňuje vývojárom jemne kontrolovať spôsob spracovania JSON.
Hlavné rozdiely v sémantike/správaní
Rozdiely v správaní medzi v1 a v2 sú zhrnuté položku po položke, pričom sa zameriavame na súbor v2_test.go
.
1. Zhoda názvov polí (rozlišovanie veľkých a malých písmen)
- Správanie v1: Pri unmarshalovaní členov JSON objektu do polí štruktúry Go sa zhoda vykonáva bez rozlišovania veľkých a malých písmen (case-insensitive).
"FirstName"
aj"firstname"
sa mapujú na poleFirstName
. - Správanie v2: Predvolene sa zhoda vykonáva s rozlišovaním veľkých a malých písmen (case-sensitive) a mapujú sa iba presne zhodné polia.
- Dôvod zmeny: Zhoda bez rozlišovania veľkých a malých písmen môže byť príčinou neočakávaného správania a môže viesť k zníženiu výkonu pri spracovaní nezhodných polí. v2 prijalo ako predvolené správanie jasnejšie a predvídateľnejšie správanie.
- Súvisiace možnosti: Vo v2 je možné explicitne povoliť ignorovanie veľkých a malých písmen pre jednotlivé polia pomocou tagu
json:"...,case:ignore"
, alebo použiť možnosťjson.MatchCaseInsensitiveNames(true)
globálne.
2. Zmena významu tagu omitempty
- Správanie v1: Polia sa vynechávajú na základe "prázdneho stavu" hodnoty Go. "Prázdny stav" tu znamená
false
,0
,nil
ukazovateľ/rozhranie, pole/slice/map/reťazec s nulovou dĺžkou. - Správanie v2: Polia sa vynechávajú na základe "prázdneho stavu" kódovanej hodnoty JSON. To znamená, že sa vynechajú, ak by boli kódované ako
null
,""
,{}
,[]
. - Dôvod zmeny: Definícia v1 je závislá od typového systému Go. v2 poskytuje konzistentnejšie správanie tým, že sa riadi typovým systémom JSON. Napríklad vo v1 sa hodnota
false
typubool
vynecháva, zatiaľ čo vo v2 safalse
nevynecháva, pretože nie je prázdnou hodnotou JSON. Vo v2 je pridaná možnosťomitzero
, ktorá môže nahradiť správanieomitempty
z v1, ktoré sa vzťahovalo na0
alebofalse
. - Súvisiace možnosti: Ak chcete vo v2 dosiahnuť rovnaké správanie ako vo v1, použite možnosť
json.OmitEmptyWithLegacyDefinition(true)
.
3. Zmena správania tagu string
- Správanie v1: Aplikuje sa na polia typu číslo, boolean, reťazec. Danú hodnotu znova zakóduje do JSON reťazca (napr.
int(42)
->"42"
). Rekurzívne sa neaplikuje na hodnoty vo vnútri zložených typov (slice, map atď.). - Správanie v2: Aplikuje sa iba na číselné typy a aplikuje sa rekurzívne. To znamená, že aj čísla vo vnútri slice ako
[]int
budú všetky zakódované ako JSON reťazce. - Dôvod zmeny: Hlavným účelom možnosti
string
je reprezentovať čísla ako reťazce, aby sa zabránilo strate presnosti 64-bitových celých čísel. Správanie v1 bolo obmedzené a nekonzistentné. v2 sa zameriava na tento kľúčový účel a rekurzívne rozširuje správanie, aby bolo užitočnejšie. - Súvisiace možnosti: Možnosť
json.StringifyWithLegacySemantics(true)
môže simulovať správanie v1.
4. Marshalovanie nil
slice a map
- Správanie v1:
nil
slice anil
map sa marshalujú akonull
. - Správanie v2: Predvolene sa
nil
slice marshaluje ako[]
(prázdne pole) anil
map ako{}
(prázdny objekt). - Dôvod zmeny:
nil
je implementačný detail jazyka Go a nie je žiaduce ho vystavovať formátu JSON, ktorý je nezávislý od jazyka.[]
alebo{}
na reprezentáciu prázdnych kolekcií sú univerzálnejšie výrazy. - Súvisiace možnosti: Vo v2 je možné marshalovať ako
null
rovnako ako vo v1 pomocou možnostíjson.FormatNilSliceAsNull(true)
alebojson.FormatNilMapAsNull(true)
.
5. Unmarshalovanie poľa
- Správanie v1: Pri unmarshalovaní do Go poľa (
[N]T
) sa nevygeneruje chyba, aj keď sa dĺžka JSON poľa líši od dĺžky Go poľa. Ak je dĺžka kratšia, zvyšný priestor sa vyplní nulovými hodnotami; ak je dlhšia, prebytok sa zahodí. - Správanie v2: Dĺžka JSON poľa sa musí presne zhodovať s dĺžkou Go poľa. V opačnom prípade sa vygeneruje chyba.
- Dôvod zmeny: V Go majú polia s pevnou veľkosťou často významný význam svojej dĺžky. Správanie v1 môže viesť k tichej strate dát. v2 zvyšuje presnosť pomocou prísnejších pravidiel.
- Súvisiace možnosti: Možnosť
json.UnmarshalArrayFromAnyLength(true)
môže simulovať správanie v1.
6. Spracovanie time.Duration
- Správanie v1:
time.Duration
sa interne považuje zaint64
a kóduje sa ako JSON číslo v nanosekundách. - Správanie v2: Kóduje sa ako JSON reťazec vo formáte ako
"1h2m3s"
pomocou metódytime.Duration.String()
. - Dôvod zmeny: Numerické nanosekundy sú menej čitateľné a štandardná reťazcová reprezentácia
time.Duration
je užitočnejšia. - Súvisiace možnosti: Správanie v1 je možné použiť pomocou tagu
json:",format:nano"
alebo možnostijson.FormatTimeWithLegacySemantics(true)
.
7. Spracovanie neplatného UTF-8
- Správanie v1: Pri marshalovaní/unmarshalovaní, ak sa v reťazci nachádzajú neplatné UTF-8 bajty, ticho sa nahradia náhradným znakom Unicode (
\uFFFD
). - Správanie v2: Predvolene sa vráti chyba, ak sa stretne s neplatným UTF-8.
- Dôvod zmeny: Zabránenie tichému poškodeniu dát a dodržiavanie prísnejšieho štandardu JSON (RFC 7493).
- Súvisiace možnosti: Správanie v1 je možné simulovať pomocou možnosti
jsontext.AllowInvalidUTF8(true)
.
8. Spracovanie duplicitných názvov členov objektu
- Správanie v1: Povoľuje duplicitné názvy členov v JSON objekte. Hodnota, ktorá sa objaví naposledy, prepíše predchádzajúce.
- Správanie v2: Predvolene sa vráti chyba, ak sa nájdu duplicitné názvy členov.
- Dôvod zmeny: Štandard RFC 8259 nedefinuje správanie duplicitných názvov, čo môže viesť k rozdielnemu správaniu medzi implementáciami. To môže byť zdrojom bezpečnostných zraniteľností. v2 to explicitne odmieta, čím zvyšuje presnosť a bezpečnosť.
- Súvisiace možnosti: Správanie v1 je možné simulovať pomocou možnosti
jsontext.AllowDuplicateNames(true)
.
Rozdiely v implementácii a architektúre
- v1: Silne závisí od
decodeState
vdecode.go
a ručne napísaného stavového automatu (state machine) vscanner.go
. Ide o monolitickú štruktúru, kde sú parsovacie logiky a sémantická analýza silne prepojené. - v2: Architektúra je modulárnejšia.
encoding/json/jsontext
: Poskytuje nízkoúrovňový, vysoko výkonný JSON tokenizer (Decoder
) a encoder (Encoder
). Tento balík sa zameriava iba na syntaktické aspekty JSON.encoding/json/v2
: Spracováva sémantickú konverziu medzi typmi Go a hodnotami JSON na základejsontext
.- Toto oddelenie analýzy syntaxe a sémantiky zlepšuje prehľadnosť kódu a výkon.
Nové API a funkcie v2
v2 poskytuje veľmi flexibilné ovládacie prvky prostredníctvom systému json.Options
.
json.Options
: Súbor možností, ktoré menia správanie marshalovania/unmarshalovania.json.JoinOptions(...)
: Spája viacero možností do jednej.WithMarshalers
/WithUnmarshalers
: Výkonná funkcia, ktorá umožňuje vstreknúť logiku serializácie/deserializácie pre konkrétne typy bez implementácie rozhraníMarshaler
/Unmarshaler
. Toto je obzvlášť užitočné pri spracovaní typov z externých balíkov.- Nové možnosti:
RejectUnknownMembers
,Deterministic(false)
,FormatNilSliceAsNull
a mnohé ďalšie ovládacie prvky správania, ktoré vo v1 neboli možné.
Záver
encoding/json
v2 je moderná implementácia, ktorá výrazne zlepšuje presnosť, výkon a flexibilitu na základe skúseností s v1. Hoci sú predvolené operácie prísnejšie, sofistikovaný systém Options
plne podporuje všetky operácie v1, čo umožňuje postupne zavádzať výhody v2 pri zachovaní kompatibility s existujúcim kódom.
- Pre nové projekty sa odporúča používať v2 ako predvolené.
- Existujúce projekty môžu naďalej používať
jsonv1
alebo migrovať najsonv2
a postupne zavádzať prísnejšie správanie v2 prostredníctvomDefaultOptionsV1()
.