---
id: 61f2009278a5f7366f79395da59447cf
author: lemonmint
title: Analys av beteendeförändringar i Go 1.25 encoding/json/v2 och kompatibilitetslagret för v1
description: Vi jämför de skillnader som introducerats i encoding/json/v2, en experimentell funktion i Go 1.25 (goexperiment.jsonv2), avseende skiftlägeskänslig matchning, förändrad semantik för omitempty och omitzero, serialisering av nil-slice och nil-map samt tillgängliga alternativ.
language: sv
date: 2025-06-20T05:38:20.100520237Z
path: /blog/posts/go-1-25-encoding-json-v1-vs-v2-z74a36b08
category: release
---

Paketet `encoding/json` v2 i Go är en ny implementation som syftar till att förbättra flera brister i den befintliga v1 (bristande konsekvens, överraskande beteenden, prestandaproblem). Den aktiveras via byggtaggen `goexperiment.jsonv2` och är en experimentell funktion.

Det viktigaste är att **när v2 är aktiverat fungerar v1 som ett kompatibilitetslager ovanpå v2-implementationen, som emulerar v1:s beteende**. Detta sker via funktionen `DefaultOptionsV1()` i filen `v2_v1.go`. Det innebär att v2 tillhandahåller alternativ som helt kan återskapa v1:s beteende, samtidigt som den erbjuder nya standardbeteenden som är strängare och mer förutsägbara.

Huvudmålen för v2 är följande.

1. **Förbättrad korrekthet och förutsägbarhet**: Genom strängare standardregler (till exempel skiftlägeskänslighet och förbud mot dubbletter av nycklar) minskas oväntade beteenden.
2. **Förbättrad prestanda**: Parsnings- och kodningsmotorerna har konstruerats om för ökad effektivitet.
3. **Ökad flexibilitet och kontroll**: Ett detaljerat alternativsystem införs så att utvecklare kan finjustera hur JSON-bearbetningen sker.

---

## Viktiga semantiska och beteendemässiga skillnader

Skillnaderna mellan v1 och v2 sammanfattas nedan utifrån filen `v2_test.go`.

### 1. Matchning av fältnamn (skiftlägeskänslighet)

- **v1-beteende**: Vid avmarshalling av JSON-objektmedlemmar till Go-structfält **ignoreras skiftläge (case-insensitive)** vid matchningen. Både `"FirstName"` och `"firstname"` mappas till fältet `FirstName`.
- **v2-beteende**: Som standard är matchningen **skiftlägeskänslig (case-sensitive)** och mappar endast fält som matchar exakt.
- **Orsak till ändringen**: Skiftlägesoberoende matchning kan orsaka oväntade beteenden och medför prestandaförluster när icke-matchande fält ska hanteras. v2 har därför antagit ett tydligare och mer förutsägbart standardbeteende.
- **Relaterade alternativ**: I v2 kan skiftlägesoberoende matchning aktiveras explicit för enskilda fält med taggalternativet `json:"...,case:ignore"`, eller globalt genom alternativet `json.MatchCaseInsensitiveNames(true)`.

### 2. Förändrad betydelse av taggalternativet `omitempty`

- **v1-beteende**: Fält utelämnas utifrån Go-värdets "tomma tillstånd". Med "tomt tillstånd" avses `false`, `0`, `nil`-pekare eller gränssnitt samt arrayer, slice:ar, maps och strängar med längden noll.
- **v2-beteende**: Fält utelämnas utifrån det kodade JSON-värdets "tomma tillstånd". Det innebär att fältet utelämnas om det kodas som `null`, `""`, `{}` eller `[]`.
- **Orsak till ändringen**: v1:s definition är beroende av Go:s typsystem. v2 utgår i stället från JSON:s typsystem och ger därför ett mer konsekvent beteende. Till exempel utelämnas i v1 värdet `false` för en `bool`, medan det i v2 inte utelämnas eftersom `false` är ett icke-tomt JSON-värde. I v2 kan alternativet `omitzero` användas för att ersätta v1:s beteende där `omitempty` gällde för `0` eller `false`.
- **Relaterade alternativ**: För att i v2 få samma beteende som i v1 används alternativet `json.OmitEmptyWithLegacyDefinition(true)`.

### 3. Förändrat beteende för taggalternativet `string`

- **v1-beteende**: Tillämpas på fält av typerna tal, booleska värden och strängar. Värdet kodas på nytt inuti en JSON-sträng (till exempel `int(42)` -> `"42"`). Det tillämpas inte rekursivt på värden inuti sammansatta typer (slice:ar, maps med mera).
- **v2-beteende**: Tillämpas **endast på numeriska typer** och tillämpas rekursivt. Det innebär att även tal inuti slice:ar, till exempel `[]int`, kodas som JSON-strängar.
- **Orsak till ändringen**: Det huvudsakliga syftet med alternativet `string` är att förhindra precisionsförlust för 64-bitars heltal genom att representera talen som strängar. v1:s beteende var begränsat och inkonsekvent. v2 koncentrerar sig på detta kärnsyfte och utökar beteendet rekursivt för att göra det mer användbart.
- **Relaterade alternativ**: Med alternativet `json.StringifyWithLegacySemantics(true)` kan v1:s beteende efterliknas.

### 4. Marshalling av `nil`-slice:ar och maps

- **v1-beteende**: `nil`-slice:ar och `nil`-maps marshalleras som `null`.
- **v2-beteende**: Som standard marshalleras `nil`-slice:ar som `[]` (tom array) och `nil`-maps som `{}` (tomt objekt).
- **Orsak till ändringen**: `nil` är en implementationsdetalj i Go-språket, och det är inte önskvärt att exponera detta i ett språkoberoende JSON-format. `[]` och `{}` är vanligare representationer av tomma samlingar.
- **Relaterade alternativ**: I v2 kan marshalling till `null`, likt v1, uppnås med alternativen `json.FormatNilSliceAsNull(true)` eller `json.FormatNilMapAsNull(true)`.

### 5. Avmarshalling av arrayer

- **v1-beteende**: Vid avmarshalling till Go-arrayer (`[N]T`) ger en JSON-array vars längd avviker från Go-arrayens längd inget fel. Om JSON-arrayen är kortare fylls de återstående platserna med nollvärden, och om den är längre kastas det överskjutande bort.
- **v2-beteende**: JSON-arrayens längd **måste exakt överensstämma** med Go-arrayens längd. Annars uppstår ett fel.
- **Orsak till ändringen**: I Go har arrayer med fast storlek ofta en betydelsefull längd. v1:s beteende kan orsaka tyst dataförlust. v2 har genom striktare regler ökat korrektheten.
- **Relaterade alternativ**: Med alternativet `json.UnmarshalArrayFromAnyLength(true)` kan v1:s beteende efterliknas.

### 6. Hantering av `time.Duration`

- **v1-beteende**: `time.Duration` behandlas internt som `int64` och kodas som ett JSON-tal i nanosekunder.
- **v2-beteende**: Kodas som en JSON-sträng i formatet `"1h2m3s"` med hjälp av metoden `time.Duration.String()`.
- **Orsak till ändringen**: Tal i nanosekunder är svåra att läsa, och den standardiserade strängrepresentationen av `time.Duration` är mer användbar.
- **Relaterade alternativ**: v1:s beteende kan användas med taggalternativet `json:",format:nano"` eller alternativet `json.FormatTimeWithLegacySemantics(true)`.

### 7. Hantering av ogiltig UTF-8

- **v1-beteende**: Vid marshalling och avmarshalling ersätts tyst ogiltiga UTF-8-bytes i strängar med Unicodes ersättningstecken (`\uFFFD`).
- **v2-beteende**: Som standard returneras ett **fel** när ogiltig UTF-8 påträffas.
- **Orsak till ändringen**: Syftet är att förhindra tyst datakorruption och att följa den striktare JSON-standarden (RFC 7493).
- **Relaterade alternativ**: Med alternativet `jsontext.AllowInvalidUTF8(true)` kan v1:s beteende efterliknas.

### 8. Hantering av dubbla medlemsnamn i objekt

- **v1-beteende**: JSON-objekt får innehålla medlemmar med samma namn. Det sist förekommande värdet skriver över de tidigare.
- **v2-beteende**: Som standard returneras ett **fel** om dubbla medlemsnamn förekommer.
- **Orsak till ändringen**: RFC 8259 definierar inte beteendet vid dubbla namn, vilket kan leda till olika beteenden mellan implementationer. Detta kan vara en källa till säkerhetsbrister. v2 avvisar därför dubbletterna explicit för ökad korrekthet och säkerhet.
- **Relaterade alternativ**: Med alternativet `jsontext.AllowDuplicateNames(true)` kan v1:s beteende efterliknas.

---

## Skillnader i implementation och arkitektur

- **v1**: Förlitar sig i hög grad på `decodeState` i `decode.go` och en manuellt skriven tillståndsmaskin (state machine) i `scanner.go`. Detta är en monolitisk struktur där parsningslogik och semantisk analys är starkt kopplade.
- **v2**: Arkitekturen är mer modulär.
  - **`encoding/json/jsontext`**: Tillhandahåller en lågnivå-tokenizer (`Decoder`) och kodare (`Encoder`) med hög prestanda. Paketet fokuserar enbart på JSON:s syntaktiska aspekter.
  - **`encoding/json/v2`**: Bygger på `jsontext` och hanterar den semantiska omvandlingen mellan Go-typer och JSON-värden.
  - Genom denna uppdelning separeras syntaktisk analys från semantisk analys, vilket förbättrar kodens tydlighet och prestanda.

---

## Nya API:er och funktioner i v2

v2 erbjuder mycket flexibel kontroll via systemet `json.Options`.

- **`json.Options`**: En samling alternativ som ändrar marshalling- och avmarshallingbeteendet.
- **`json.JoinOptions(...)`**: Slår samman flera alternativ till ett.
- **`WithMarshalers` / `WithUnmarshalers`**: Ett kraftfullt sätt att injicera serialiserings- och deserialiseringslogik för specifika typer, utan att typerna behöver implementera gränssnitten `Marshaler` eller `Unmarshaler`. Detta är särskilt användbart vid hantering av typer från externa paket.
- **Nya alternativ**: `RejectUnknownMembers`, `Deterministic(false)`, `FormatNilSliceAsNull` med flera ger kontroll över beteenden som inte var möjliga i v1.

---

## Slutsats

`encoding/json` v2 är en modern implementation som, med utgångspunkt i erfarenheterna från v1, avsevärt förbättrar **korrekthet, prestanda och flexibilitet**. Standardbeteendet har blivit strängare, men genom det avancerade systemet `Options` kan v1:s alla beteenden fullt ut återskapas. Det gör det möjligt att behålla kompatibilitet med befintlig kod och successivt införa v2:s fördelar.

- För **nya projekt** rekommenderas att v2 används som standard.
- **Befintliga projekt** kan fortsätta använda `jsonv1` som tidigare, eller migrera till `jsonv2` och successivt införa v2:s strängare beteende med hjälp av `DefaultOptionsV1()`.
