---
id: 61f2009278a5f7366f79395da59447cf
author: lemonmint
title: Analyse av endringer i atferd i Go 1.25 encoding/json/v2 og kompatibilitetslaget for v1
description: Denne artikkelen sammenligner encoding/json/v2, introdusert som et eksperimentelt Go 1.25-funksjon (goexperiment.jsonv2), med hensyn til skiftlandsfølsom feltmatching, endret semantikk for omitempty og omitzero, serialisering av nil-slices og nil-maps, og tilhørende alternativer.
language: "no"
date: 2025-06-20T05:38:20.100520237Z
path: /blog/posts/go-1-25-encoding-json-v1-vs-v2-z74a36b08
category: release
---

Go-pakken `encoding/json` v2 er en ny implementasjon som er ment å forbedre flere ulemper ved den eksisterende v1 (manglende konsistens, overraskende oppførsel, ytelsesproblemer). Den aktiveres som en eksperimentell funksjon via byggetaggen `goexperiment.jsonv2`.

Det viktigste å merke seg er at **når v2 er aktivert, fungerer v1 som et kompatibilitetslag som emulerer v1-oppførselen oppå v2-implementasjonen**. Dette skjer via funksjonen `DefaultOptionsV1()` i filen `v2_v1.go`. Med andre ord tilbyr v2 alternativer som kan reprodusere v1-oppførselen fullt ut, samtidig som den introduserer strengere og mer forutsigbare nye standardinnstillinger.

Hovedmålene for v2 er som følger.

1. **Økt korrekthet og forutsigbarhet**: Strengere regler som standard (for eksempel skille mellom store og små bokstaver, og forbud mot dupliserte nøkler) reduserer uventet oppførsel.
2. **Forbedret ytelse**: Parse- og kodingsmotorene er redesignet for økt effektivitet.
3. **Større fleksibilitet og kontroll**: Et detaljert alternativsystem gjør det mulig for utviklere å styre JSON-behandlingen nøyaktig.

---

## Viktige forskjeller i semantikk og oppførsel

Forskjellene mellom v1 og v2 er oppsummert punkt for punkt, med utgangspunkt i filen `v2_test.go`.

### 1. Matching av feltnavn (skille mellom store og små bokstaver)

- **v1-oppførsel**: Ved unmarshaling av JSON-objektmedlemmer til Go-structfelt **ignoreres forskjeller i store og små bokstaver (case-insensitive)**. Både `"FirstName"` og `"firstname"` knyttes til feltet `FirstName`.
- **v2-oppførsel**: Som standard **skilles det mellom store og små bokstaver (case-sensitive)**, og bare felt som samsvarer nøyaktig, blir knyttet til.
- **Begrunnelse for endringen**: Matching som ignorerer store og små bokstaver kan føre til uventet oppførsel og medfører ytelsestap når felt uten treff må håndteres. v2 har valgt en tydeligere og mer forutsigbar oppførsel som standard.
- **Relaterte alternativer**: I v2 kan man eksplisitt aktivere matching uten hensyn til store og små bokstaver for enkeltfelt med taggalternativet `json:"...,case:ignore"`, eller globalt med `json.MatchCaseInsensitiveNames(true)`.

### 2. Endret betydning av taggalternativet `omitempty`

- **v1-oppførsel**: Felt utelates basert på Go-verdiens «tomme tilstand». Med «tom tilstand» menes `false`, `0`, `nil`-pekere eller grensesnitt, samt arrayer, slices, maps og strenger med lengde null.
- **v2-oppførsel**: Felt utelates basert på den kodede JSON-verdiens «tomme tilstand». Det vil si at felt utelates når de kodes som `null`, `""`, `{}` eller `[]`.
- **Begrunnelse for endringen**: v1-definisjonen er avhengig av Gos typesystem. v2 tar utgangspunkt i JSON-typesystemet og gir dermed mer konsistent oppførsel. For eksempel utelates verdien `false` for en `bool` i v1, mens `false` i v2 ikke utelates, fordi det er en ikke-tom JSON-verdi. I v2 er det lagt til alternativet `omitzero`, som kan erstatte den tidligere oppførselen der `omitempty` gjaldt for `0` og `false`.
- **Relaterte alternativer**: For å oppnå nøyaktig samme oppførsel som v1 i v2 brukes alternativet `json.OmitEmptyWithLegacyDefinition(true)`.

### 3. Endret oppførsel for taggalternativet `string`

- **v1-oppførsel**: Gjelder for felt av typene tall, boolske verdier og strenger. Verdien kodes på nytt inn i en JSON-streng (for eksempel `int(42)` -> `"42"`). Verdier inne i sammensatte typer (slices, maps osv.) påvirkes ikke rekursivt.
- **v2-oppførsel**: **Gjelder bare for tallfelt** og anvendes rekursivt. Det vil si at også tall inne i en slice, for eksempel `[]int`, kodes som JSON-strenger.
- **Begrunnelse for endringen**: Hovedformålet med alternativet `string` er å forhindre tap av presisjon for 64-bits heltall ved å representere tall som strenger. Oppførselen i v1 var begrenset og lite konsistent. v2 fokuserer på dette kjerneformålet og utvider oppførselen rekursivt for å gjøre den mer nyttig.
- **Relaterte alternativer**: Oppførselen i v1 kan etterlignes med `json.StringifyWithLegacySemantics(true)`.

### 4. Marshaling av `nil`-slices og -maps

- **v1-oppførsel**: `nil`-slices og `nil`-maps marshales som `null`.
- **v2-oppførsel**: Som standard marshales `nil`-slices som `[]` (tom array) og `nil`-maps som `{}` (tomt objekt).
- **Begrunnelse for endringen**: `nil` er en implementasjonsdetalj i Go-språket, og det er lite ønskelig å eksponere den i et språkuavhengig JSON-format. `[]` og `{}` er mer vanlige uttrykk for tomme samlinger.
- **Relaterte alternativer**: I v2 kan man marshale til `null`, slik som i v1, med `json.FormatNilSliceAsNull(true)` eller `json.FormatNilMapAsNull(true)`.

### 5. Unmarshaling av arrayer

- **v1-oppførsel**: Ved unmarshaling til Go-arrayer (`[N]T`) gir det ingen feil om lengden på JSON-arrayen avviker fra lengden på Go-arrayen. Hvis JSON-arrayen er kortere, fylles resten med nullverdier, og hvis den er lengre, kastes overskytende elementer.
- **v2-oppførsel**: Lengden på JSON-arrayen må **stemme nøyaktig** overens med lengden på Go-arrayen. Hvis ikke, oppstår en feil.
- **Begrunnelse for endringen**: I Go har faste arrayer ofte en lengde som er meningsbærende. Oppførselen i v1 kan føre til stille tap av data. v2 bruker strengere regler for å øke korrektheten.
- **Relaterte alternativer**: Oppførselen i v1 kan etterlignes med alternativet `json.UnmarshalArrayFromAnyLength(true)`.

### 6. Håndtering av `time.Duration`

- **v1-oppførsel**: `time.Duration` behandles internt som `int64` og kodes som et JSON-tall i nanosekunder.
- **v2-oppførsel**: Verdien kodes som en JSON-streng i formatet `"1h2m3s"` ved bruk av metoden `time.Duration.String()`.
- **Begrunnelse for endringen**: Tall i nanosekunder er vanskelige å lese, og den standardiserte strengrepresentasjonen av `time.Duration` er mer nyttig.
- **Relaterte alternativer**: Oppførselen i v1 kan brukes med taggalternativet `json:",format:nano"` eller med alternativet `json.FormatTimeWithLegacySemantics(true)`.

### 7. Håndtering av ugyldig UTF-8

- **v1-oppførsel**: Ved marshaling og unmarshaling erstattes ugyldige UTF-8-bytes i strenger **stille** med Unicode-erstatningstegnet (`\uFFFD`).
- **v2-oppførsel**: Som standard returneres en **feil** når ugyldig UTF-8 påtreffes.
- **Begrunnelse for endringen**: Dette forhindrer stille datakorrupsjon og er i tråd med den strengere JSON-standarden RFC 7493.
- **Relaterte alternativer**: Oppførselen i v1 kan etterlignes med alternativet `jsontext.AllowInvalidUTF8(true)`.

### 8. Håndtering av dupliserte medlemsnavn i objekter

- **v1-oppførsel**: Et JSON-objekt kan inneholde medlemmer med samme navn flere ganger. Den siste verdien overskriver de tidligere.
- **v2-oppførsel**: Som standard returneres en **feil** dersom det finnes dupliserte medlemsnavn.
- **Begrunnelse for endringen**: RFC 8259 definerer ikke oppførselen ved dupliserte navn, slik at implementasjoner kan oppføre seg ulikt. Dette kan være en kilde til sikkerhetssårbarheter. v2 avviser dette eksplisitt for å øke korrektheten og sikkerheten.
- **Relaterte alternativer**: Oppførselen i v1 kan etterlignes med alternativet `jsontext.AllowDuplicateNames(true)`.

---

## Forskjeller i implementasjon og arkitektur

- **v1**: Er i stor grad avhengig av `decodeState` i `decode.go` og en manuelt skrevet tilstandsmaskin i `scanner.go`. Dette er en monolittisk struktur der parselogikk og semantisk analyse er tett koblet sammen.
- **v2**: Arkitekturen er mer modulær.
  - **`encoding/json/jsontext`**: Tilbyr en lavnivå og høyytelses JSON-tokenizer (`Decoder`) og koder (`Encoder`). Denne pakken fokuserer utelukkende på JSONs syntaktiske sider.
  - **`encoding/json/v2`**: Bygger på `jsontext` og håndterer den semantiske konverteringen mellom Go-typer og JSON-verdier.
  - Ved å skille disse lagene fra hverandre er syntaktisk og semantisk analyse atskilt, noe som gir bedre kodeklarhet og ytelse.

---

## Nye API-er og funksjoner i v2

v2 tilbyr svært fleksibel kontroll gjennom alternativsystemet `json.Options`.

- **`json.Options`**: En samling av alternativer som endrer oppførselen ved marshaling og unmarshaling.
- **`json.JoinOptions(...)`**: Slår flere alternativer sammen til ett.
- **`WithMarshalers` / `WithUnmarshalers`**: En kraftig funksjon som gjør det mulig å injisere serialiserings- og deserialiseringslogikk for en bestemt type, uten at typen må implementere grensesnittene `Marshaler` eller `Unmarshaler`. Dette er særlig nyttig når man må håndtere typer fra eksterne pakker.
- **Nye alternativer**: `RejectUnknownMembers`, `Deterministic(false)`, `FormatNilSliceAsNull` og andre gir kontroll over oppførsel som ikke var mulig i v1.

---

## Konklusjon

`encoding/json` v2 er en moderne implementasjon som bygger på erfaringene fra v1 og i betydelig grad forbedrer **korrekthet, ytelse og fleksibilitet**. Standardoppførselen er blitt strengere, men det avanserte `Options`-systemet gjør det mulig å reprodusere alle v1-oppførsler fullt ut. Dermed kan eksisterende kode fortsatt fungere, samtidig som man gradvis tar i bruk fordelene med v2.

- **Nye prosjekter** bør som hovedregel bruke v2.
- **Eksisterende prosjekter** kan fortsette å bruke `jsonv1` uendret, eller migrere til `jsonv2` og innføre v2s strengere oppførsel gradvis ved hjelp av `DefaultOptionsV1()`.
