GoSuda

Porovnanie Go 1.25 encoding/json v1 vs v2

By lemonmint
views ...

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é:

  1. 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.
  2. Zlepšenie výkonu: Prepracovaním parsovacieho a kódovacieho enginu sa zvýšila efektivita.
  3. 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 pole FirstName.
  • 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 typu bool vynecháva, zatiaľ čo vo v2 sa false nevynecháva, pretože nie je prázdnou hodnotou JSON. Vo v2 je pridaná možnosť omitzero, ktorá môže nahradiť správanie omitempty z v1, ktoré sa vzťahovalo na 0 alebo false.
  • 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 a nil map sa marshalujú ako null.
  • Správanie v2: Predvolene sa nil slice marshaluje ako [] (prázdne pole) a nil 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) alebo json.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 za int64 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ódy time.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žnosti json.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 v decode.go a ručne napísaného stavového automatu (state machine) v scanner.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áklade jsontext.
    • 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ť na jsonv2 a postupne zavádzať prísnejšie správanie v2 prostredníctvom DefaultOptionsV1().