Go 1.25 encoding/json v1 vs v2 sammenligning
Go sin encoding/json
-pakke v2 er en ny implementasjon ment å forbedre flere av manglene ved den eksisterende v1-versjonen (mangel på konsistens, uventet oppførsel, ytelsesproblemer). Dette er en eksperimentell funksjon som aktiveres via goexperiment.jsonv2
-byggemerket.
Det viktigste punktet er at når v2 er aktivert, fungerer v1 som et kompatibilitetslag som emulerer v1s oppførsel over v2-implementasjonen. Dette oppnås via DefaultOptionsV1()
-funksjonen i v2_v1.go
-filen. Med andre ord tilbyr v2 alternativer for å perfekt gjenskape v1s oppførsel, samtidig som den presenterer en ny standardoppførsel som er strengere og mer forutsigbar.
Hovedmålene for v2 er som følger:
- Forbedret nøyaktighet og forutsigbarhet: Ved å anvende strengere regler som standard (f.eks. skille mellom store og små bokstaver, forbud mot dupliserte nøkler), reduseres uventet oppførsel.
- Ytelsesforbedringer: Omdesign av parsings- og kodingmotorene har økt effektiviteten.
- Økt fleksibilitet og kontroll: Innføring av et detaljert Options-system gjør at utviklere kan kontrollere JSON-behandlingen på en detaljert måte.
Hovedforskjeller i betydning/oppførsel
Forskjellene i oppførsel mellom v1 og v2 er organisert etter kategori, med fokus på v2_test.go
-filen.
1. Feltnavn-matching (skille mellom store og små bokstaver)
- v1-oppførsel: Ved unmarshalling av JSON-objektmedlemmer til Go-struktfelt, matches det uten å skille mellom store og små bokstaver (case-insensitive). Både
"FirstName"
og"firstname"
mappes tilFirstName
-feltet. - v2-oppførsel: Som standard matches det med skille mellom store og små bokstaver (case-sensitive), og bare eksakt matchende felt mappes.
- Årsak til endring: Matching uten skille mellom store og små bokstaver kan føre til uventet oppførsel og forårsake ytelsesforringelse ved håndtering av ikke-matchende felt. v2 har tatt i bruk en klarere og mer forutsigbar oppførsel som standard.
- Relaterte opsjoner: I v2 kan man eksplisitt aktivere case-insensitive matching per felt ved å bruke
json:"...,case:ignore"
tag-opsjonen, eller anvendejson.MatchCaseInsensitiveNames(true)
-opsjonen globalt.
2. Endring i betydningen av omitempty
tag-opsjonen
- v1-oppførsel: Felt utelates basert på Go-verdiens "tomme tilstand". "Tom tilstand" refererer her til
false
,0
,nil
-pekere/interfacer, og arrays/slices/maps/strenger med lengde null. - v2-oppførsel: Felt utelates basert på den "tomme tilstanden" til den kodede JSON-verdien. Det vil si at hvis den kodes som
null
,""
,{}
,[]
, vil den utelates. - Årsak til endring: V1s definisjon er avhengig av Go-typesystemet. V2 tilbyr mer konsistent oppførsel basert på JSON-typesystemet. For eksempel, i v1 utelates
false
-verdien avbool
-typen, mens i v2 utelates ikkefalse
da det er en ikke-tom JSON-verdi. V2 legger tilomitzero
-opsjonen for å erstatte v1somitempty
-oppførsel som ble brukt på0
ellerfalse
. - Relaterte opsjoner: Hvis man ønsker samme oppførsel som v1 i v2, bruker man
json.OmitEmptyWithLegacyDefinition(true)
-opsjonen.
3. Endring i oppførselen til string
tag-opsjonen
- v1-oppførsel: Anvendes på numeriske felt, boolske felt og strengfelt. Koder verdien på nytt inne i en JSON-streng (f.eks.
int(42)
->"42"
). Anvendes ikke rekursivt på verdier inne i sammensatte typer (slices, maps, etc.). - v2-oppførsel: Anvendes kun på numeriske typer, og anvendes rekursivt. Det vil si at tall inne i slices som
[]int
også kodes som JSON-strenger. - Årsak til endring: Hovedformålet med
string
-opsjonen er å representere tall som strenger for å unngå presisjonstap for 64-biters heltall. V1s oppførsel var begrenset og manglet konsistens. V2 fokuserer på denne kjernebruken og utvider oppførselen rekursivt for å gjøre den mer nyttig. - Relaterte opsjoner:
json.StringifyWithLegacySemantics(true)
-opsjonen kan brukes til å etterligne v1s oppførsel.
4. Marshalling av nil
slices og maps
- v1-oppførsel:
nil
slices ognil
maps marshalles somnull
. - v2-oppførsel: Som standard marshalles
nil
slices som[]
(tom array), ognil
maps som{}
(tomt objekt). - Årsak til endring:
nil
er en implementasjonsdetalj i Go-språket, og det er ikke ønskelig å eksponere dette i det språkuavhengige JSON-formatet.[]
eller{}
for å representere tomme samlinger er en mer universell representasjon. - Relaterte opsjoner: I v2 kan man marshallere som
null
som i v1 ved hjelp avjson.FormatNilSliceAsNull(true)
ellerjson.FormatNilMapAsNull(true)
-opsjonen.
5. Array Unmarshalling
- v1-oppførsel: Ved unmarshalling til en Go-array (
[N]T
), genereres det ingen feil selv om lengden på JSON-arrayen er forskjellig fra lengden på Go-arrayen. Hvis lengden er kortere, fylles den gjenværende plassen med nullverdier, og hvis den er lengre, forkastes overskytende elementer. - v2-oppførsel: Lengden på JSON-arrayen må nøyaktig samsvare med lengden på Go-arrayen. Hvis ikke, oppstår det en feil.
- Årsak til endring: I Go har faste størrelser på arrays ofte en viktig betydning. V1s oppførsel kan føre til stilltiende tap av data. V2 øker nøyaktigheten med strengere regler.
- Relaterte opsjoner:
json.UnmarshalArrayFromAnyLength(true)
-opsjonen kan brukes til å etterligne v1s oppførsel.
6. Håndtering av time.Duration
- v1-oppførsel:
time.Duration
behandles internt som enint64
og kodes som et JSON-tall i nanosekunder. - v2-oppførsel: Kodes som en JSON-streng i formatet
"1h2m3s"
ved bruk avtime.Duration.String()
-metoden. - Årsak til endring: Nanosekunder som tall er mindre lesbare, og standard strengrepresentasjon av
time.Duration
er mer nyttig. - Relaterte opsjoner: Man kan bruke v1s oppførsel via
json:",format:nano"
tag-opsjonen ellerjson.FormatTimeWithLegacySemantics(true)
-opsjonen.
7. Håndtering av ugyldig UTF-8
- v1-oppførsel: Ved marshalling/unmarshalling, hvis det finnes ugyldige UTF-8-bytes i en streng, erstattes de stille med Unicode-erstatningstegnet (
\uFFFD
). - v2-oppførsel: Som standard returneres det en feil hvis ugyldig UTF-8 oppdages.
- Årsak til endring: For å forhindre stille datakorrupsjon og for å følge den strengere JSON-standarden (RFC 7493).
- Relaterte opsjoner:
jsontext.AllowInvalidUTF8(true)
-opsjonen kan brukes til å etterligne v1s oppførsel.
8. Håndtering av dupliserte objektmedlemsnavn
- v1-oppførsel: Tillater at medlemmer med samme navn vises duplisert i et JSON-objekt. Den sist oppståtte verdien overskriver de tidligere.
- v2-oppførsel: Som standard returneres det en feil hvis det er dupliserte medlemsnavn.
- Årsak til endring: RFC 8259-standarden definerer ikke oppførselen til dupliserte navn, noe som kan føre til ulik oppførsel mellom implementasjoner. Dette kan være en kilde til sikkerhetssårbarheter. V2 avviser dette eksplisitt for å øke nøyaktigheten og sikkerheten.
- Relaterte opsjoner:
jsontext.AllowDuplicateNames(true)
-opsjonen kan brukes til å etterligne v1s oppførsel.
Forskjeller i implementasjon og arkitektur
- v1: Er sterkt avhengig av
decodeState
idecode.go
og den manuelt skrevne tilstandsmaskinen iscanner.go
. Dette er en monolittisk struktur der parsingslogikken og semantisk analyse er sterkt kombinert. - v2: Arkitekturen er mer modulær.
encoding/json/jsontext
: Tilbyr en lavnivå, høytytende JSON-tokeniser (Decoder
) og koder (Encoder
). Denne pakken fokuserer kun på den syntaktiske siden av JSON.encoding/json/v2
: Håndterer den semantiske konverteringen mellom Go-typer og JSON-verdier basert påjsontext
.- Denne separasjonen av syntaktisk og semantisk analyse har forbedret kodens klarhet og ytelse.
Nye API-er og funksjoner i v2
v2 tilbyr svært fleksible kontrollfunksjoner gjennom json.Options
-systemet.
json.Options
: Et sett med opsjoner som endrer marshalling-/unmarshalling-oppførselen.json.JoinOptions(...)
: Slår sammen flere opsjoner til én.WithMarshalers
/WithUnmarshalers
: En kraftig funksjon som lar deg injisere serialiserings-/deserialiseringslogikk for spesifikke typer uten å implementereMarshaler
/Unmarshaler
-grensesnittene. Dette er spesielt nyttig når du håndterer typer fra eksterne pakker.- Nye opsjoner: Ulike oppførselskontroller som ikke var mulige i v1, er nå tilgjengelige, for eksempel
RejectUnknownMembers
,Deterministic(false)
,FormatNilSliceAsNull
.
Konklusjon
encoding/json
v2 er en moderne implementasjon som er sterkt forbedret med tanke på nøyaktighet, ytelse og fleksibilitet basert på erfaringene fra v1. Selv om standardoppførselen er blitt strengere, støtter det sofistikerte Options
-systemet fullt ut all v1s oppførsel, slik at man gradvis kan innføre fordelene med v2 samtidig som kompatibiliteten med eksisterende kode opprettholdes.
- For nye prosjekter anbefales det å bruke v2 som standard.
- Eksisterende prosjekter kan fortsette å bruke
jsonv1
som det er, eller migrere tiljsonv2
og gradvis innføre v2s strengere oppførsel gjennomDefaultOptionsV1()
.