Go 1.25 encoding/json v1 vs v2 vergelijking
Go's encoding/json
pakket v2 is een nieuwe implementatie die diverse tekortkomingen van de bestaande v1 versie (gebrek aan consistentie, verrassend gedrag, prestatieproblemen) verbetert. Dit is een experimentele functionaliteit die geactiveerd wordt via de goexperiment.jsonv2
build tag.
Het belangrijkste punt is dat wanneer v2 geactiveerd is, v1 functioneert als een compatibiliteitslaag die het gedrag van v1 emuleert bovenop de v2 implementatie. Dit wordt gerealiseerd via de DefaultOptionsV1()
functie in het v2_v1.go
bestand. Dit betekent dat v2 opties biedt om het gedrag van v1 perfect te reproduceren, terwijl het tegelijkertijd een nieuw, strikter en voorspelbaarder standaardgedrag introduceert.
De hoofddoelstellingen van v2 zijn als volgt:
- Verbeterde nauwkeurigheid en voorspelbaarheid: Door standaard striktere regels toe te passen (bijvoorbeeld hoofdlettergevoeligheid, verbod op dubbele sleutels) wordt onverwacht gedrag verminderd.
- Prestatieverbetering: De parsing- en encoding-engine is opnieuw ontworpen om de efficiëntie te verhogen.
- Verhoogde flexibiliteit en controle: Er is een gedetailleerd optiesysteem geïntroduceerd waarmee ontwikkelaars de JSON-verwerkingsmethode nauwkeurig kunnen controleren.
Belangrijkste Semantische/Gedragsverschillen
De gedragsverschillen tussen v1 en v2 zijn categorisch samengevat, met de nadruk op het v2_test.go
bestand.
1. Veldnaam Matching (Hoofdlettergevoeligheid)
- v1 Gedrag: Bij het unmarshalling van JSON objectleden naar Go structuurvelden, wordt er hoofdletterongevoelig (case-insensitive) gematcht. Zowel
"FirstName"
als"firstname"
worden gemapt naar hetFirstName
veld. - v2 Gedrag: Standaard wordt er hoofdlettergevoelig (case-sensitive) gematcht, waarbij alleen exact overeenkomende velden worden gemapt.
- Reden voor Wijziging: Hoofdletterongevoelige matching kan de oorzaak zijn van onverwacht gedrag en leidt tot prestatievermindering bij het verwerken van niet-overeenkomende velden. v2 heeft een duidelijker en voorspelbaarder gedrag als standaard aangenomen.
- Gerelateerde Opties: In v2 kan hoofdletterongevoeligheid expliciet per veld worden geactiveerd met de
json:"...,case:ignore"
tag optie, of globaal worden toegepast met dejson.MatchCaseInsensitiveNames(true)
optie.
2. Wijziging in de Betekenis van de omitempty
Tag Optie
- v1 Gedrag: Velden worden weggelaten op basis van de "lege staat" van de Go waarde. Hierbij betekent "lege staat"
false
,0
,nil
pointers/interfaces, en arrays/slices/maps/strings met lengte 0. - v2 Gedrag: Velden worden weggelaten op basis van de "lege staat" van de gecodeerde JSON waarde. Dat wil zeggen, ze worden weggelaten indien ze als
null
,""
,{}
,[]
worden gecodeerd. - Reden voor Wijziging: De definitie van v1 is afhankelijk van het type systeem van Go. v2 biedt consistenter gedrag door uit te gaan van het JSON type systeem. Bijvoorbeeld, in v1 wordt een
false
waarde van het typebool
weggelaten, maar in v2 wordtfalse
niet weggelaten omdat het een niet-lege JSON waarde is. In v2 is deomitzero
optie toegevoegd om het gedrag van v1'somitempty
dat van toepassing was op0
offalse
te vervangen. - Gerelateerde Opties: Indien in v2 hetzelfde gedrag als v1 gewenst is, gebruikt men de
json.OmitEmptyWithLegacyDefinition(true)
optie.
3. Wijziging in het Gedrag van de string
Tag Optie
- v1 Gedrag: Van toepassing op velden van numerieke, booleaanse en string-typen. De betreffende waarde wordt opnieuw gecodeerd binnen een JSON string (bijv.
int(42)
->"42"
). Dit wordt niet recursief toegepast op waarden binnen complexe typen (slices, maps, enz.). - v2 Gedrag: Is alleen van toepassing op numerieke typen en wordt recursief toegepast. Dat wil zeggen, alle getallen binnen een slice zoals
[]int
worden ook als JSON strings gecodeerd. - Reden voor Wijziging: Het primaire doel van de
string
optie is om precisieverlies bij 64-bit integers te voorkomen door getallen als strings weer te geven. Het gedrag van v1 was beperkt en inconsistent. v2 concentreert zich op dit kerndoel en breidt het gedrag recursief uit om het nuttiger te maken. - Gerelateerde Opties: Met de
json.StringifyWithLegacySemantics(true)
optie kan het gedrag van v1 worden nagebootst.
4. nil
Slice en Map Marshalling
- v1 Gedrag:
nil
slices ennil
maps worden gemarshald alsnull
. - v2 Gedrag: Standaard worden
nil
slices gemarshald als[]
(lege array) ennil
maps als{}
(leeg object). - Reden voor Wijziging:
nil
is een implementatiedetail van de Go taal, en het blootstellen hiervan aan het taal-onafhankelijke JSON-formaat is onwenselijk. Lege verzamelingen, aangeduid door[]
of{}
, zijn een meer universele representatie. - Gerelateerde Opties: In v2 kan, net als in v1, gemarshald worden als
null
via dejson.FormatNilSliceAsNull(true)
ofjson.FormatNilMapAsNull(true)
optie.
5. Array Unmarshalling
- v1 Gedrag: Bij het unmarshalling naar een Go array (
[N]T
), wordt er geen fout gegenereerd, zelfs niet als de lengte van de JSON array afwijkt van de lengte van de Go array. Indien korter, wordt de resterende ruimte opgevuld met nulwaarden; indien langer, worden de overtollige elementen verworpen. - v2 Gedrag: De lengte van de JSON array moet exact overeenkomen met de lengte van de Go array. Anders wordt er een fout gegenereerd.
- Reden voor Wijziging: In Go is de lengte van fixed-size arrays vaak van cruciaal belang. Het gedrag van v1 kon leiden tot stil dataverlies. v2 verhoogt de nauwkeurigheid door striktere regels te hanteren.
- Gerelateerde Opties: Met de
json.UnmarshalArrayFromAnyLength(true)
optie kan het gedrag van v1 worden nagebootst.
6. time.Duration
Verwerking
- v1 Gedrag:
time.Duration
wordt intern behandeld alsint64
en gecodeerd als een JSON-nummer in nanoseconden. - v2 Gedrag: Wordt gecodeerd als een JSON string in het formaat
"1h2m3s"
met behulp van detime.Duration.String()
methode. - Reden voor Wijziging: Numerieke nanoseconden zijn minder leesbaar, en de standaard stringweergave van
time.Duration
is nuttiger. - Gerelateerde Opties: Het gedrag van v1 kan worden gebruikt via de
json:",format:nano"
tag optie of dejson.FormatTimeWithLegacySemantics(true)
optie.
7. Verwerking van Ongeldige UTF-8
- v1 Gedrag: Bij marshalling/unmarshalling, indien ongeldige UTF-8 bytes aanwezig zijn in een string, worden deze stilzwijgend vervangen door het Unicode vervangingskarakter (
\uFFFD
). - v2 Gedrag: Standaard wordt er een fout geretourneerd bij het aantreffen van ongeldige UTF-8.
- Reden voor Wijziging: Dit is om stilzwijgende datacorruptie te voorkomen en om te voldoen aan de striktere JSON-standaard (RFC 7493).
- Gerelateerde Opties: Met de
jsontext.AllowInvalidUTF8(true)
optie kan het gedrag van v1 worden nagebootst.
8. Verwerking van Dubbele Objectlidnamen
- v1 Gedrag: Dubbele lidnamen binnen een JSON-object zijn toegestaan. De laatst voorkomende waarde overschrijft de eerdere.
- v2 Gedrag: Standaard wordt er een fout geretourneerd indien er dubbele lidnamen zijn.
- Reden voor Wijziging: De RFC 8259-standaard definieert het gedrag van dubbele namen niet, waardoor implementaties verschillend kunnen reageren. Dit kan een bron zijn van beveiligingskwetsbaarheden. v2 weigert dit expliciet om de nauwkeurigheid en veiligheid te verhogen.
- Gerelateerde Opties: Met de
jsontext.AllowDuplicateNames(true)
optie kan het gedrag van v1 worden nagebootst.
Implementatie- en Architectuurverschillen
- v1: Is sterk afhankelijk van
decodeState
indecode.go
en een handmatig geschreven state machine inscanner.go
. Dit is een monolithische structuur waarbij parsing logica en semantische analyse sterk met elkaar verweven zijn. - v2: De architectuur is meer gemodulariseerd.
encoding/json/jsontext
: Biedt een laag-niveau, hoogwaardige JSON tokenizer (Decoder
) en encoder (Encoder
). Dit pakket concentreert zich uitsluitend op de syntactische aspecten van JSON.encoding/json/v2
: Behandelt de semantische conversie tussen Go typen en JSON waarden, gebaseerd opjsontext
.- Deze scheiding van syntactische en semantische analyse verbetert de helderheid van de code en de prestaties.
Nieuwe API en Functionaliteiten van v2
v2 biedt zeer flexibele controlefunctionaliteiten via het json.Options
systeem.
json.Options
: Een verzameling van opties die het marshalling/unmarshalling gedrag wijzigen.json.JoinOptions(...)
: Voegt meerdere opties samen tot één.WithMarshalers
/WithUnmarshalers
: Een krachtige functionaliteit die het injecteren van serialisatie/deserialisatie logica voor specifieke typen mogelijk maakt, zonder dat deMarshaler
/Unmarshaler
interface geïmplementeerd hoeft te worden. Dit is bijzonder nuttig bij het verwerken van typen uit externe pakketten.- Nieuwe Opties: Diverse gedragscontroles die in v1 niet mogelijk waren, zoals
RejectUnknownMembers
,Deterministic(false)
,FormatNilSliceAsNull
, zijn nu beschikbaar.
Conclusie
encoding/json
v2 is een moderne implementatie die, voortbouwend op de ervaringen van v1, de nauwkeurigheid, prestaties en flexibiliteit aanzienlijk heeft verbeterd. Hoewel het standaardgedrag strikter is geworden, ondersteunt het geavanceerde Options
systeem alle gedragingen van v1 volledig, waardoor compatibiliteit met bestaande code behouden blijft en de voordelen van v2 geleidelijk kunnen worden geïntroduceerd.
- Voor nieuwe projecten wordt aanbevolen v2 als standaard te gebruiken.
- Bestaande projecten kunnen
jsonv1
blijven gebruiken, of een strategie hanteren om geleidelijk de striktere gedragingen van v2 te introduceren viaDefaultOptionsV1()
tijdens de migratie naarjsonv2
.