---
id: 61f2009278a5f7366f79395da59447cf
author: lemonmint
title: Go 1.25 encoding/json/v2 käyttäytymismuutokset ja v1-yhteensopivuuskerroksen analyysi
description: Vertaamme encoding/json/v2:n kirjainkokoon huomioivaa vastaavuutta, omitempty- ja omitzero-määritteiden merkityksen muutoksia, nil-viipaleiden ja -karttojen serialisointia sekä asetuksia, jotka on esitelty Go 1.25:n kokeellisena ominaisuutena (goexperiment.jsonv2).
language: fi
date: 2025-06-20T05:38:20.100520237Z
path: /blog/posts/go-1-25-encoding-json-v1-vs-v2-z74a36b08
category: release
---

Go-kielen `encoding/json`-paketin v2 on uusi toteutus, jonka tavoitteena on parantaa vanhan v1:n useita heikkouksia (johdonmukaisuuden puute, yllättävä käyttäytyminen, suorituskykyongelmat). Se on kokeellinen ominaisuus, joka otetaan käyttöön `goexperiment.jsonv2`-koontilipun kautta.

Tärkeintä on, että **kun v2 on käytössä, v1 toimii yhteensopivuuskerroksena, joka emuloi v1:n käyttäytymistä v2-toteutuksen päällä**. Tämä toteutuu `v2_v1.go`-tiedoston `DefaultOptionsV1()`-funktion kautta. Toisin sanoen v2 tarjoaa asetukset, joilla v1:n käyttäytyminen voidaan toistaa täydellisesti, ja samalla se esittelee tiukemmat ja ennustettavammat uudet oletuskäyttäytymiset.

v2:n pääasialliset tavoitteet ovat seuraavat:

1. **Tarkkuuden ja ennustettavuuden parantaminen**: Oletuksena sovelletaan tiukempia sääntöjä (esimerkiksi isot ja pienet kirjaimet erottava vertailu, kielletyt kaksoiskaviot), mikä vähentää odottamatonta käyttäytymistä.
2. **Suorituskyvyn parantaminen**: Jäsennys- ja koodausmoottori on suunniteltu uudelleen tehokkuuden lisäämiseksi.
3. **Joustavuuden ja hallinnan laajentaminen**: Yksityiskohtainen asetusjärjestelmä mahdollistaa kehittäjille JSON-käsittelyn hienojakoisen hallinnan.

---

## Keskeiset merkitys- ja käyttäytymiserot

Erot v1:n ja v2:n välillä on koottu kohdittain, pääpainon ollessa `v2_test.go`-tiedostossa.

### 1. Kenttänimien vastaavuus (isot ja pienet kirjaimet)

- **v1-käyttäytyminen**: Kun JSON-olion jäseniä unmarshalloidaan Go-rakenteen kenttiin, vastaavuus tehdään **kirjainkoosta riippumatta (case-insensitive)**. Sekä `"FirstName"` että `"firstname"` kartoittuvat kenttään `FirstName`.
- **v2-käyttäytyminen**: Oletuksena vastaavuus on **kirjainkokoherkkä (case-sensitive)**, ja kenttään kartoittuu vain tarkasti täsmäävä nimi.
- **Muutoksen syy**: Kirjainkoosta riippumaton vastaavuus voi aiheuttaa odottamatonta käyttäytymistä, ja se heikentää suorituskykyä käsiteltäessä kenttiä, jotka eivät täsmää. v2 valitsi oletukseksi selkeämmän ja ennustettavamman käyttäytymisen.
- **Liittyvät asetukset**: v2:ssa voidaan käyttää `json:"...,case:ignore"`-tagiasetusta, jolla kirjainkoosta riippumaton vastaavuus otetaan eksplisiittisesti käyttöön kentittäin, tai `json.MatchCaseInsensitiveNames(true)`-asetusta, joka otetaan käyttöön globaalisti.

### 2. `omitempty`-tagiasetuksen merkityksen muutos

- **v1-käyttäytyminen**: Kenttä jätetään pois Go-arvon "tyhjän tilan" perusteella. Tyhjä tila tarkoittaa tässä arvoja `false`, `0`, `nil`-osoitinta tai -liitäntää sekä pituudeltaan nollaa olevaa taulukkoa, viipaletta, karttaa tai merkkijonoa.
- **v2-käyttäytyminen**: Kenttä jätetään pois koodatun JSON-arvon "tyhjän tilan" perusteella. Eli jos arvo koodautuu muotoon `null`, `""`, `{}` tai `[]`, kenttä jätetään pois.
- **Muutoksen syy**: v1:n määritelmä on sidoksissa Go-tyyppijärjestelmään. v2 perustuu JSON-tyyppijärjestelmään ja tarjoaa siten johdonmukaisemman käyttäytymisen. Esimerkiksi v1:ssä `bool`-tyypin arvo `false` jätetään pois, mutta v2:ssa `false` on JSON-arvo, joka ei ole tyhjä, eikä sitä jätetä pois. v2:ssa on lisätty `omitzero`-asetus, jolla voidaan korvata v1:n `omitempty`-käyttäytyminen, joka koski arvoja `0` ja `false`.
- **Liittyvät asetukset**: Jos halutaan v1:n kanssa identtinen käyttäytyminen, käytetään asetusta `json.OmitEmptyWithLegacyDefinition(true)`.

### 3. `string`-tagiasetuksen käyttäytymisen muutos

- **v1-käyttäytyminen**: Sovelletaan kenttiin, joiden tyyppi on numero, totuusarvo tai merkkijono. Arvo koodataan uudelleen JSON-merkkijonon sisälle (esimerkiksi `int(42)` -> `"42"`). Yhdistettyjen tyyppien (viipaleet, kartat jne.) sisäisiin arvoihin ei sovelleta asetusta rekursiivisesti.
- **v2-käyttäytyminen**: Sovelletaan **vain numerotyyppeihin**, ja asetus on rekursiivinen. Eli myös esimerkiksi `[]int`-viipaleen sisäiset numerot koodataan kaikki JSON-merkkijonoiksi.
- **Muutoksen syy**: `string`-asetuksen pääasiallinen käyttötarkoitus on estää 64-bittisten kokonaislukujen tarkkuuden menetys esittämällä numerot merkkijonoina. v1:n käyttäytyminen oli rajoittunutta ja johdonmukaisuudeltaan puutteellista. v2 keskittyy tähän keskeiseen käyttötarkoitukseen ja laajentaa käyttäytymistä rekursiivisesti, jolloin asetuksesta tuli hyödyllisempi.
- **Liittyvät asetukset**: v1:n käyttäytymistä voidaan jäljitellä asetuksella `json.StringifyWithLegacySemantics(true)`.

### 4. `nil`-viipaleiden ja -karttojen marshalointi

- **v1-käyttäytyminen**: `nil`-viipale ja `nil`-kartta marshalloidaan muotoon `null`.
- **v2-käyttäytyminen**: Oletuksena `nil`-viipale marshalloidaan muotoon `[]` (tyhjä taulukko) ja `nil`-kartta muotoon `{}` (tyhjä olio).
- **Muutoksen syy**: `nil` on Go-kielen toteutuksen yksityiskohta, eikä sen paljastaminen kielestä riippumattomassa JSON-muodossa ole toivottavaa. Tyhjiä kokoelmia esittävät `[]` ja `{}` ovat yleisempiä ilmaisuja.
- **Liittyvät asetukset**: v2:ssa voidaan marshalloida v1:n tapaan `null`-arvoksi käyttämällä asetuksia `json.FormatNilSliceAsNull(true)` tai `json.FormatNilMapAsNull(true)`.

### 5. Taulukon unmarshalointi

- **v1-käyttäytyminen**: Kun JSON-taulukko unmarshalloidaan Go-taulukkoon (`[N]T`), virhettä ei synny, vaikka JSON-taulukon pituus poikkeaisi Go-taulukon pituudesta. Jos JSON-taulukko on lyhyempi, ylimääräinen tila täytetään nollla-arvoilla, ja jos se on pidempi, ylimenevä osa hylätään.
- **v2-käyttäytyminen**: JSON-taulukon pituuden on **täsmättävä tarkalleen** Go-taulukon pituuteen. Muussa tapauksessa syntyy virhe.
- **Muutoksen syy**: Go-kielessä kiinteäkokoisen taulukon pituudella on usein merkittävä merkitys. v1:n käyttäytyminen voi aiheuttaa datan äänetöntä katoamista. v2 nostaa tarkkuutta tiukemmalla säännöllä.
- **Liittyvät asetukset**: v1:n käyttäytymistä voidaan jäljitellä asetuksella `json.UnmarshalArrayFromAnyLength(true)`.

### 6. `time.Duration`-tyypin käsittely

- **v1-käyttäytyminen**: `time.Duration` käsitellään sisäisesti `int64`-tyyppinä, ja se koodataan nanosekuntiyksikössä olevaksi JSON-numeroksi.
- **v2-käyttäytyminen**: Koodaus tehdään `time.Duration.String()`-metodin avulla JSON-merkkijonona muodossa, kuten `"1h2m3s"`.
- **Muutoksen syy**: Nanosekuntimuotoiset numerot ovat vaikeasti luettavia, ja `time.Duration`-tyypin vakiomerkkijonoesitys on hyödyllisempi.
- **Liittyvät asetukset**: v1:n käyttäytymisen voi ottaa käyttöön tagiasetuksella `json:",format:nano"` tai asetuksella `json.FormatTimeWithLegacySemantics(true)`.

### 7. Virheellisen UTF-8:n käsittely

- **v1-käyttäytyminen**: Marshalloinnin ja unmarshaloinnin aikana merkkijonossa olevat virheelliset UTF-8-tavut korvataan äänettömästi Unicode-korvaavalla merkillä (`\uFFFD`).
- **v2-käyttäytyminen**: Oletuksena virheellinen UTF-8 aiheuttaa **virheen**.
- **Muutoksen syy**: Tarkoituksena on estää datan äänetön vioittuminen ja noudattaa tiukempaa JSON-standardia (RFC 7493).
- **Liittyvät asetukset**: v1:n käyttäytymistä voidaan jäljitellä asetuksella `jsontext.AllowInvalidUTF8(true)`.

### 8. Kaksoiskappaleiden käsittely objektin jäsennimissä

- **v1-käyttäytyminen**: JSON-oliossa sallitaan saman nimisten jäsenten esiintyminen useaan kertaan. Viimeisenä esiintyvä arvo ylikirjoittaa aiemmat.
- **v2-käyttäytyminen**: Oletuksena kaksoiskappaleena esiintyvä jäsennimi aiheuttaa **virheen**.
- **Muutoksen syy**: RFC 8259 -standardi ei määrittele kaksoiskappaleiden käsittelyä, joten eri toteutukset voivat toimia eri tavoin. Tämä voi olla tietoturvahaavoittuvuuden lähde. v2 hylkää ne eksplisiittisesti, mikä parantaa tarkkuutta ja tietoturvaa.
- **Liittyvät asetukset**: v1:n käyttäytymistä voidaan jäljitellä asetuksella `jsontext.AllowDuplicateNames(true)`.

---

## Toteutus- ja arkkitehtuurierot

- **v1**: Luottaa voimakkaasti `decode.go`-tiedoston `decodeState`-rakenteeseen ja `scanner.go`-tiedoston käsin kirjoitettuun tilakoneeseen. Tämä on jäsennyslogiikan ja semanttisen analyysin tiukasti yhdistävä monoliittinen rakenne.
- **v2**: Arkkitehtuuri on modulaarisempi.
  - **`encoding/json/jsontext`**: Tarjoaa matalan tason, suorituskykyisen JSON-tokenisoijan (`Decoder`) ja kooderin (`Encoder`). Tämä paketti keskittyy ainoastaan JSON-syntaksin puoleen.
  - **`encoding/json/v2`**: Käsittelee `jsontext`-paketin pohjalta Go-tyyppien ja JSON-arvojen välisen semanttisen muunnoksen.
  - Tämän jaottelun ansiosta syntaksianalyysi ja semanttinen analyysi on erotettu toisistaan, mikä on parantanut koodin selkeyttä ja suorituskykyä.

---

## v2:n uudet API:t ja ominaisuudet

v2 tarjoaa erittäin joustavia hallintaominaisuuksia `json.Options`-asetusjärjestelmän kautta.

- **`json.Options`**: Joukko asetuksia, jotka muuttavat marshalointi- ja unmarshalointikäyttäytymistä.
- **`json.JoinOptions(...)`**: Yhdistää useita asetuksia yhdeksi.
- **`WithMarshalers` / `WithUnmarshalers`**: Tehokas ominaisuus, jolla serialisointi- ja deserialisointilogiikka voidaan lisätä tietylle tyypille ilman, että tyyppi toteuttaa `Marshaler`- tai `Unmarshaler`-rajapintaa. Tämä on erityisen hyödyllistä, kun käsitellään ulkoisten pakettien tyyppejä.
- **Uudet asetukset**: `RejectUnknownMembers`, `Deterministic(false)`, `FormatNilSliceAsNull` ja muut mahdollistavat käyttäytymisen hallinnan tavalla, joka ei ollut mahdollinen v1:ssä.

---

## Yhteenveto

`encoding/json` v2 on nykyaikainen toteutus, joka perustuu v1:n kokemuksiin ja parantaa merkittävästi **tarkkuutta, suorituskykyä ja joustavuutta**. Oletuskäyttäytyminen on tiukempi, mutta hienostuneen `Options`-järjestelmän ansiosta v1:n kaikki käyttäytymiset voidaan toteuttaa täysin, joten olemassa oleva koodi säilyy yhteensopivana ja v2:n etuja voidaan ottaa käyttöön vaiheittain.

- **Uusi projekti** kannattaa toteuttaa oletuksena v2:lla.
- **Olemassa olevat projektit** voivat joko käyttää `jsonv1`-pakettia sellaisenaan tai siirtyä `jsonv2`-pakettiin ja ottaa v2:n tiukemman käyttäytymisen vaiheittain käyttöön `DefaultOptionsV1()`-funktion avulla.
