GoSuda

Go 1.25 encoding/json v1 vs v2 összehasonlítás

By lemonmint
views ...

A Go encoding/json csomagjának v2-es verziója egy új implementáció, amely a korábbi v1 számos hiányosságát (konzisztenciahiány, meglepő viselkedés, teljesítményproblémák) hivatott orvosolni. Ez egy kísérleti funkció, amelyet a goexperiment.jsonv2 build tag-en keresztül lehet aktiválni.

A legfontosabb szempont, hogy ha a v2 aktiválva van, a v1 egy kompatibilitási rétegként működik, amely a v1 viselkedését emulálja a v2 implementáció felett. Ez a v2_v1.go fájlban található DefaultOptionsV1() funkcióval valósul meg. Ez azt jelenti, hogy a v2 olyan opciókat kínál, amelyekkel a v1 viselkedése tökéletesen reprodukálható, ugyanakkor szigorúbb és kiszámíthatóbb új alapértelmezett viselkedést vezet be.

A v2 fő céljai a következők:

  1. Pontosság és kiszámíthatóság javítása: Alapértelmezés szerint szigorúbb szabályok (pl. kis- és nagybetűk megkülönböztetése, duplikált kulcsok tiltása) alkalmazásával csökkenti a váratlan viselkedéseket.
  2. Teljesítmény javítása: Az elemző és kódoló motor újratervezésével növelték a hatékonyságot.
  3. Rugalmasság és nagyobb kontroll: Részletes opciórendszer bevezetésével lehetővé teszi a fejlesztők számára a JSON-feldolgozás finomhangolását.

Fő jelentésbeli/viselkedésbeli különbségek

A v2_test.go fájl alapján rendszereztük a v1 és v2 közötti viselkedésbeli különbségeket.

1. Mezőnév illesztés (kis- és nagybetűk megkülönböztetése)

  • v1 viselkedés: Amikor JSON objektumtagokat Go struktúra mezőkhöz unmarshal-ol, nem tesz különbséget a kis- és nagybetűk között (case-insensitive) az illesztés során. Mind a "FirstName", mind a "firstname" a FirstName mezőhöz lesz hozzárendelve.
  • v2 viselkedés: Alapértelmezés szerint megkülönbözteti a kis- és nagybetűket (case-sensitive), és csak a pontosan egyező mezőket képezi le.
  • Változás oka: A kis- és nagybetűket nem megkülönböztető illesztés váratlan viselkedéseket okozhat, és teljesítménycsökkenéshez vezethet az illesztés nélküli mezők kezelésekor. A v2 egyértelműbb és kiszámíthatóbb viselkedést fogadott el alapértelmezésként.
  • Kapcsolódó opciók: A v2-ben a json:"...,case:ignore" tag opcióval mezőnként explicit módon engedélyezhető a kis- és nagybetűk figyelmen kívül hagyása, vagy a json.MatchCaseInsensitiveNames(true) opció globálisan alkalmazható.

2. Az omitempty tag opció jelentésének megváltozása

  • v1 viselkedés: A Go érték "üres állapota" alapján hagyja el a mezőket. Az "üres állapot" itt false, 0, nil pointer/interfész, nulla hosszúságú tömb/slice/map/string.
  • v2 viselkedés: A kódolt JSON érték "üres állapota" alapján hagyja el a mezőket. Ez azt jelenti, hogy ha null, "", {}, [] formában kódolódik, akkor elhagyásra kerül.
  • Változás oka: A v1 definíciója a Go típusrendszerétől függ. A v2 a JSON típusrendszerét veszi alapul, és így konzisztensebb viselkedést biztosít. Például a v1-ben a bool típusú false érték elhagyásra kerül, de a v2-ben a false nem üres JSON érték, ezért nem hagyja el. A v2-ben hozzáadták az omitzero opciót, amely a v1 omitempty opciójának 0-ra vagy false-ra vonatkozó viselkedését helyettesíti.
  • Kapcsolódó opciók: Ha a v1-gyel azonos viselkedést szeretne a v2-ben, használja a json.OmitEmptyWithLegacyDefinition(true) opciót.

3. A string tag opció viselkedésének megváltozása

  • v1 viselkedés: Szám, logikai és string típusú mezőkre alkalmazható. Az adott értéket újra kódolja egy JSON stringbe (pl. int(42) -> "42"). Az összetett típusokon (slice, map stb.) belüli értékekre rekurzívan nem alkalmazható.
  • v2 viselkedés: Csak szám típusokra alkalmazható, és rekurzívan. Ez azt jelenti, hogy a []int típusú slice-on belüli számok is JSON stringként lesznek kódolva.
  • Változás oka: A string opció fő célja a számok stringként való megjelenítése a 64 bites egészek pontosságvesztésének elkerülése érdekében. A v1 viselkedése korlátozott és inkonzisztens volt. A v2 erre a kulcsfontosságú felhasználási esetre összpontosít, és rekurzívan kiterjeszti a viselkedését, hogy hasznosabbá tegye.
  • Kapcsolódó opciók: A json.StringifyWithLegacySemantics(true) opcióval a v1 viselkedése utánozható.

4. nil slice és map marshalling

  • v1 viselkedés: A nil slice-ok és nil map-ek null értékként vannak marshal-olva.
  • v2 viselkedés: Alapértelmezés szerint a nil slice-ok [] (üres tömb), a nil map-ek pedig {} (üres objektum) értékként vannak marshal-olva.
  • Változás oka: A nil a Go nyelv implementációs részlete, és nem kívánatos ezt a nyelvtől független JSON formátumban feltárni. Az üres gyűjteményeket jelölő [] vagy {} gyakoribb kifejezések.
  • Kapcsolódó opciók: A v2-ben a json.FormatNilSliceAsNull(true) vagy json.FormatNilMapAsNull(true) opcióval a v1-hez hasonlóan null értékként marshal-olható.

5. Tömb unmarshalling

  • v1 viselkedés: Go tömbbe ([N]T) unmarshalling-oláskor nem okoz hibát, ha a JSON tömb hossza eltér a Go tömb hosszától. Ha rövidebb, a fennmaradó helyeket nulla értékekkel tölti fel; ha hosszabb, a felesleget elveti.
  • v2 viselkedés: A JSON tömb hosszának pontosan meg kell egyeznie a Go tömb hosszával. Ellenkező esetben hiba lép fel.
  • Változás oka: Go-ban a fix méretű tömbök hossza gyakran fontos jelentőséggel bír. A v1 viselkedése csendes adatvesztést okozhat. A v2 szigorúbb szabályokkal növeli a pontosságot.
  • Kapcsolódó opciók: A json.UnmarshalArrayFromAnyLength(true) opcióval a v1 viselkedése utánozható.

6. time.Duration kezelés

  • v1 viselkedés: A time.Duration belsőleg int64-ként kezelődik, és nanoszekundumban kifejezett JSON számként kódolódik.
  • v2 viselkedés: A time.Duration.String() metódust használja, és JSON stringként kódolódik, például "1h2m3s" formában.
  • Változás oka: A nanoszekundumok számként való ábrázolása nehezen olvasható, és a time.Duration szabványos string reprezentációja hasznosabb.
  • Kapcsolódó opciók: A json:",format:nano" tag opcióval vagy a json.FormatTimeWithLegacySemantics(true) opcióval a v1 viselkedése használható.

7. Érvénytelen UTF-8 kezelése

  • v1 viselkedés: Marshalling/unmarshalling során, ha érvénytelen UTF-8 bájtok találhatók a stringben, azok csendben kicserélődnek Unicode helyettesítő karakterre (\uFFFD).
  • v2 viselkedés: Alapértelmezés szerint hibát ad vissza, ha érvénytelen UTF-8-at talál.
  • Változás oka: Az adatok csendes sérülésének megelőzése és a szigorúbb JSON szabvány (RFC 7493) betartása érdekében.
  • Kapcsolódó opciók: A jsontext.AllowInvalidUTF8(true) opcióval a v1 viselkedése utánozható.

8. Duplikált objektum tagnevek kezelése

  • v1 viselkedés: Megengedi, hogy a JSON objektumon belül azonos nevű tagok duplikáltan jelenjenek meg. Az utolsóként megjelenő érték felülírja az előzőeket.
  • v2 viselkedés: Alapértelmezés szerint hibát ad vissza, ha duplikált tagnevek vannak.
  • Változás oka: Az RFC 8259 szabvány nem definiálja a duplikált nevek viselkedését, így az implementációk között eltérő lehet. Ez biztonsági rések forrása lehet. A v2 ezt explicit módon elutasítja a pontosság és a biztonság növelése érdekében.
  • Kapcsolódó opciók: A jsontext.AllowDuplicateNames(true) opcióval a v1 viselkedése utánozható.

Implementációs és architektúra-beli különbségek

  • v1: Nagyban támaszkodik a decode.go fájlban található decodeState-re és a scanner.go fájlban kézzel írt állapotgépre (state machine). Ez egy monolitikus struktúra, ahol az elemzési logika és a szemantikai elemzés szorosan kapcsolódik.
  • v2: Az architektúra modulárisabbá vált.
    • encoding/json/jsontext: Alacsony szintű, nagy teljesítményű JSON tokenizálót (Decoder) és kódolót (Encoder) biztosít. Ez a csomag kizárólag a JSON szintaktikai aspektusaira összpontosít.
    • encoding/json/v2: A jsontext alapjaira épülve kezeli a Go típusok és a JSON értékek közötti szemantikai átalakítást.
    • Ez a szétválasztás révén a szintaktikai és szemantikai elemzés különválik, javítva a kód áttekinthetőségét és a teljesítményt.

A v2 új API-jai és funkciói

A v2 a json.Options rendszeren keresztül rendkívül rugalmas vezérlési lehetőségeket kínál.

  • json.Options: A marshalling/unmarshalling viselkedését módosító opciók halmaza.
  • json.JoinOptions(...): Több opciót egyesít egybe.
  • WithMarshalers / WithUnmarshalers: Erőteljes funkció, amely lehetővé teszi a szerializálási/deszerializálási logika injektálását bizonyos típusokhoz anélkül, hogy implementálni kellene a Marshaler/Unmarshaler interfészt. Ez különösen hasznos külső csomagok típusainak kezelésekor.
  • Új opciók: RejectUnknownMembers, Deterministic(false), FormatNilSliceAsNull és számos más viselkedésvezérlés vált lehetővé, amelyek a v1-ben nem voltak elérhetők.

Összegzés

Az encoding/json v2 a v1 tapasztalataira épülve a pontosság, teljesítmény és rugalmasság terén jelentős javulást hozó modern implementáció. Bár az alapértelmezett viselkedés szigorúbbá vált, a kifinomult Options rendszer révén a v1 minden viselkedését tökéletesen támogatja, így a meglévő kóddal való kompatibilitás fenntartása mellett fokozatosan bevezethetők a v2 előnyei.

  • Új projektek esetén ajánlott a v2 alapértelmezett használata.
  • Meglévő projektek esetén a jsonv1 továbbra is használható, vagy a jsonv2-re való átállás során a DefaultOptionsV1() segítségével fokozatosan bevezethető a v2 szigorúbb viselkedése.