Go 1.25 encoding/json v1 vs v2 összehasonlítás
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:
- 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.
- Teljesítmény javítása: Az elemző és kódoló motor újratervezésével növelték a hatékonyságot.
- 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"aFirstNamemező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 ajson.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,nilpointer/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
booltípusúfalseérték elhagyásra kerül, de a v2-ben afalsenem üres JSON érték, ezért nem hagyja el. A v2-ben hozzáadták azomitzeroopciót, amely a v1omitemptyopciójának0-ra vagyfalse-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
[]inttípusú slice-on belüli számok is JSON stringként lesznek kódolva. - Változás oka: A
stringopció 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
nilslice-ok ésnilmap-eknullértékként vannak marshal-olva. - v2 viselkedés: Alapértelmezés szerint a
nilslice-ok[](üres tömb), anilmap-ek pedig{}(üres objektum) értékként vannak marshal-olva. - Változás oka: A
nila 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)vagyjson.FormatNilMapAsNull(true)opcióval a v1-hez hasonlóannullé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.Durationbelsőlegint64-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.Durationszabványos string reprezentációja hasznosabb. - Kapcsolódó opciók: A
json:",format:nano"tag opcióval vagy ajson.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.gofájlban találhatódecodeState-re és ascanner.gofá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: Ajsontextalapjaira é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 aMarshaler/Unmarshalerinterfé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
jsonv1továbbra is használható, vagy ajsonv2-re való átállás során aDefaultOptionsV1()segítségével fokozatosan bevezethető a v2 szigorúbb viselkedése.