GoSuda

Go 1.25 encoding/json v1 vs v2 sammenligning

By lemonmint
views ...

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:

  1. 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.
  2. Ytelsesforbedringer: Omdesign av parsings- og kodingmotorene har økt effektiviteten.
  3. Ø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 til FirstName-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 anvende json.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 av bool-typen, mens i v2 utelates ikke false da det er en ikke-tom JSON-verdi. V2 legger til omitzero-opsjonen for å erstatte v1s omitempty-oppførsel som ble brukt på 0 eller false.
  • 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 og nil maps marshalles som null.
  • v2-oppførsel: Som standard marshalles nil slices som [] (tom array), og nil 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 av json.FormatNilSliceAsNull(true) eller json.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 en int64 og kodes som et JSON-tall i nanosekunder.
  • v2-oppførsel: Kodes som en JSON-streng i formatet "1h2m3s" ved bruk av time.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 eller json.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 i decode.go og den manuelt skrevne tilstandsmaskinen i scanner.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 å implementere Marshaler/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 til jsonv2 og gradvis innføre v2s strengere oppførsel gjennom DefaultOptionsV1().