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"
aFirstName
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 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
,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 afalse
nem üres JSON érték, ezért nem hagyja el. A v2-ben hozzáadták azomitzero
opciót, amely a v1omitempty
opció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
[]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 ésnil
map-eknull
értékként vannak marshal-olva. - v2 viselkedés: Alapértelmezés szerint a
nil
slice-ok[]
(üres tömb), anil
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)
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.Duration
belső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.Duration
szabvá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.go
fájlban találhatódecodeState
-re és ascanner.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
: Ajsontext
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 aMarshaler
/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 ajsonv2
-re való átállás során aDefaultOptionsV1()
segítségével fokozatosan bevezethető a v2 szigorúbb viselkedése.