Jämförelse av Go 1.25 encoding/json v1 och v2
Go:s encoding/json
paket v2 är en ny implementering som syftar till att förbättra flera brister i den befintliga v1 (brist på konsekvens, överraskande beteende, prestandaproblem). Detta är en experimentell funktion som aktiveras via build-taggen goexperiment.jsonv2
.
Det viktigaste är att när v2 är aktiverat, fungerar v1 som ett kompatibilitetslager som emulerar v1:s beteende ovanpå v2-implementeringen. Detta uppnås genom funktionen DefaultOptionsV1()
i filen v2_v1.go
. Detta innebär att v2 erbjuder alternativ för att fullständigt återskapa v1:s beteende, samtidigt som det introducerar ett striktare och mer förutsägbart nytt standardbeteende.
Huvudmålen för v2 är följande:
- Förbättrad noggrannhet och förutsägbarhet: Genom att tillämpa striktare regler som standard (t.ex. skiftlägeskänslighet, förbud mot dubbla nycklar) minskas oväntade beteenden.
- Prestandaförbättringar: Parsnings- och kodningsmotorn har omdesignats för att förbättra effektiviteten.
- Ökad flexibilitet och kontroll: Ett detaljerat options-system har introducerats för att ge utvecklare finjusterad kontroll över hur JSON bearbetas.
Huvudsakliga skillnader i betydelse/beteende
Skillnaderna i beteende mellan v1 och v2 har sammanställts per punkt, med fokus på filen v2_test.go
.
1. Matchning av fältnamn (skiftlägeskänslighet)
- v1-beteende: Vid unmarshaling av JSON-objektmedlemmar till Go-strukturfält matchas de skiftlägesokänsligt (case-insensitive). Både
"FirstName"
och"firstname"
mappas till fältetFirstName
. - v2-beteende: Som standard matchas de skiftlägeskänsligt (case-sensitive), och endast exakt matchande fält mappas.
- Anledning till ändring: Skiftlägesokänslig matchning kan leda till oväntade beteenden och orsaka prestandaförsämring vid hantering av icke-matchande fält. v2 har antagit ett tydligare och mer förutsägbart beteende som standard.
- Relaterade Options: I v2 kan du explicit aktivera skiftlägesokänslighet per fält med taggoptionen
json:"...,case:ignore"
eller tillämpa optionenjson.MatchCaseInsensitiveNames(true)
globalt.
2. Ändrad betydelse av omitempty
taggoptionen
- v1-beteende: Fält utelämnas baserat på Go-värdets "tomma tillstånd". Här betyder "tomt tillstånd"
false
,0
,nil
pekare/interface, och arrayer/slices/maps/strängar med längden 0. - v2-beteende: Fält utelämnas baserat på det kodade JSON-värdets "tomma tillstånd". Det vill säga, de utelämnas om de kodas som
null
,""
,{}
,[]
. - Anledning till ändring: v1:s definition är beroende av Go:s typsystem. v2 baserar sig på JSON:s typsystem för att ge ett mer konsekvent beteende. Till exempel utelämnas ett
false
-värde av typenbool
i v1, men i v2 utelämnasfalse
inte eftersom det är ett icke-tomt JSON-värde. I v2 har optionenomitzero
lagts till för att ersätta v1:somitempty
beteende som gällde för0
ellerfalse
. - Relaterade Options: Om du vill ha samma beteende som v1 i v2, använd optionen
json.OmitEmptyWithLegacyDefinition(true)
.
3. Ändrat beteende för string
taggoptionen
- v1-beteende: Tillämpas på numeriska, booleska och strängtypsfält. Värdet kodas om till en JSON-sträng (t.ex.
int(42)
->"42"
). Det tillämpas inte rekursivt på värden inuti sammansatta typer (slices, maps, etc.). - v2-beteende: Tillämpas endast på numeriska typer och tillämpas rekursivt. Det vill säga, alla nummer inuti slices som
[]int
kommer också att kodas som JSON-strängar. - Anledning till ändring: Huvudanvändningen av
string
-optionen är att representera nummer som strängar för att undvika förlust av precision för 64-bitars heltal. v1:s beteende var begränsat och inkonsekvent. v2 fokuserar på denna kärnanvändning och utökar beteendet rekursivt för att göra det mer användbart. - Relaterade Options: Beteendet från v1 kan efterliknas med optionen
json.StringifyWithLegacySemantics(true)
.
4. Marshalling av nil
slices och maps
- v1-beteende:
nil
slices ochnil
maps marshallas somnull
. - v2-beteende: Som standard marshallas
nil
slices som[]
(tom array) ochnil
maps som{}
(tomt objekt). - Anledning till ändring:
nil
är en implementeringsdetalj i Go-språket, och det är inte önskvärt att exponera detta i det språkoberoende JSON-formatet.[]
eller{}
för att representera tomma samlingar är en mer universell representation. - Relaterade Options: I v2 kan du marshallera som
null
likt v1 genom optionernajson.FormatNilSliceAsNull(true)
ellerjson.FormatNilMapAsNull(true)
.
5. Array Unmarshaling
- v1-beteende: Vid unmarshaling till en Go-array (
[N]T
) utlöses inget fel även om JSON-arrayens längd skiljer sig från Go-arrayens längd. Om längden är kortare fylls återstående utrymme med nollvärden, och om den är längre kasseras överskottet. - v2-beteende: JSON-arrayens längd måste vara exakt lika med Go-arrayens längd. Annars uppstår ett fel.
- Anledning till ändring: I Go har arrayer med fast storlek ofta en viktig betydelse för sin längd. v1:s beteende kan leda till tyst förlust av data. v2 ökar noggrannheten med striktare regler.
- Relaterade Options: Beteendet från v1 kan efterliknas med optionen
json.UnmarshalArrayFromAnyLength(true)
.
6. Hantering av time.Duration
- v1-beteende:
time.Duration
behandlas internt som enint64
och kodas som ett JSON-nummer i nanosekunder. - v2-beteende: Använder
time.Duration.String()
-metoden för att koda som en JSON-sträng i formatet"1h2m3s"
. - Anledning till ändring: Numeriska nanosekunder är svårlästa, och den standardiserade strängrepresentationen av
time.Duration
är mer användbar. - Relaterade Options: Beteendet från v1 kan användas genom taggoptionen
json:",format:nano"
eller optionenjson.FormatTimeWithLegacySemantics(true)
.
7. Hantering av ogiltig UTF-8
- v1-beteende: Vid marshalling/unmarshaling ersätts ogiltiga UTF-8-byte i strängen tyst med Unicode-ersättningskaraktären (
\uFFFD
). - v2-beteende: Som standard returneras ett fel om ogiltig UTF-8 påträffas.
- Anledning till ändring: För att förhindra tyst datakorruption och för att följa striktare JSON-standarder (RFC 7493).
- Relaterade Options: Beteendet från v1 kan efterliknas med optionen
jsontext.AllowInvalidUTF8(true)
.
8. Hantering av dubblerade objektnamn
- v1-beteende: Tillåter att samma medlemsnamn förekommer flera gånger i ett JSON-objekt. Värdet som uppträder sist skriver över de föregående.
- v2-beteende: Som standard returneras ett fel om dubblerade medlemsnamn finns.
- Anledning till ändring: RFC 8259-standarden definierar inte beteendet för dubblerade namn, vilket kan leda till olika beteenden mellan implementeringar. Detta kan vara en källa till säkerhetsbrister. v2 förkastar detta explicit för att öka noggrannheten och säkerheten.
- Relaterade Options: Beteendet från v1 kan efterliknas med optionen
jsontext.AllowDuplicateNames(true)
.
Skillnader i implementering och arkitektur
- v1: Förlitar sig starkt på
decodeState
idecode.go
och en manuellt skriven tillståndsmaskin (state machine) iscanner.go
. Detta är en monolitisk struktur där parsninglogiken och semantisk analys är starkt kopplade. - v2: Arkitekturen är mer modulär.
encoding/json/jsontext
: Tillhandahåller en lågnivå, högpresterande JSON-tokeniserare (Decoder
) och kodare (Encoder
). Detta paket fokuserar endast på den syntaktiska aspekten av JSON.encoding/json/v2
: Bygger påjsontext
och hanterar den semantiska omvandlingen mellan Go-typer och JSON-värden.- Denna separation av syntaxanalys och semantisk analys förbättrar kodens tydlighet och prestanda.
Nya API:er och funktioner i v2
v2 erbjuder mycket flexibla kontrollfunktioner genom json.Options
-systemet.
json.Options
: En uppsättning optioner som ändrar marshalling/unmarshalling-beteendet.json.JoinOptions(...)
: Slår samman flera optioner till en.WithMarshalers
/WithUnmarshalers
: En kraftfull funktion som möjliggör injicering av serialiserings-/deserialiseringslogik för specifika typer utan att implementeraMarshaler
/Unmarshaler
-interfacet. Detta är särskilt användbart vid hantering av typer från externa paket.- Nya optioner: Olika beteendekontroller som inte var möjliga i v1, såsom
RejectUnknownMembers
,Deterministic(false)
,FormatNilSliceAsNull
, har blivit tillgängliga.
Slutsats
encoding/json
v2 är en modern implementering som, baserad på erfarenheterna från v1, har förbättrat noggrannhet, prestanda och flexibilitet avsevärt. Även om standardbeteendet har blivit striktare, stöder det sofistikerade Options
-systemet fullständigt alla v1:s beteenden, vilket möjliggör gradvis implementering av v2:s fördelar samtidigt som kompatibiliteten med befintlig kod bibehålls.
- För nya projekt är det bäst att använda v2 som standard.
- Befintliga projekt kan fortsätta att använda
jsonv1
eller använda en strategi för att gradvis introducera v2:s strikta beteende genomDefaultOptionsV1()
vid migrering tilljsonv2
.