---
id: 61f2009278a5f7366f79395da59447cf
author: lemonmint
title: Analisi delle modifiche comportamentali di encoding/json/v2 in Go 1.25 e del livello di compatibilità con v1
description: Confronta la corrispondenza sensibile alle maiuscole e minuscole, le modifiche semantiche di omitempty e omitzero, la serializzazione di slice e mappe nil e le relative opzioni nel package encoding/json/v2, introdotto come funzionalità sperimentale in Go 1.25 (goexperiment.jsonv2).
language: it
date: 2025-06-20T05:38:20.100520237Z
path: /blog/posts/go-1-25-encoding-json-v1-vs-v2-z74a36b08
category: release
---

Il pacchetto `encoding/json` v2 di Go è una nuova implementazione volta a migliorare alcuni limiti della v1 (incoerenze, comportamenti sorprendenti, problemi di prestazioni). Essa viene attivata tramite il build tag `goexperiment.jsonv2` e costituisce una funzionalità sperimentale.

Il punto più importante è che **quando la v2 è attiva, la v1 opera come un livello di compatibilità che emula il comportamento della v1 sopra l'implementazione della v2**. Ciò avviene tramite la funzione `DefaultOptionsV1()` nel file `v2_v1.go`. In altre parole, la v2 offre opzioni che consentono di riprodurre in modo completo il comportamento della v1, ma propone anche nuovi comportamenti predefiniti più rigorosi e prevedibili.

Gli obiettivi principali della v2 sono i seguenti.

1. **Miglioramento della correttezza e della prevedibilità**: applicare per impostazione predefinita regole più stringenti (ad esempio, distinzione tra maiuscole e minuscole, divieto di chiavi duplicate) per ridurre i comportamenti inattesi.
2. **Miglioramento delle prestazioni**: la progettazione dei motori di parsing e codifica è stata rivista per aumentarne l'efficienza.
3. **Maggiore flessibilità e controllo**: l'introduzione di un sistema di opzioni dettagliato consente agli sviluppatori di controllare nel dettaglio il modo in cui viene gestito il JSON.

---

## Principali differenze di significato e di comportamento

Le differenze di comportamento tra v1 e v2 sono riassunte di seguito per voce, con riferimento principale al file `v2_test.go`.

### 1. Corrispondenza dei nomi dei campi (distinzione tra maiuscole e minuscole)

- **Comportamento della v1**: durante l'unmarshaling di un membro di un oggetto JSON in un campo di una struct Go, la corrispondenza **ignora le maiuscole e le minuscole (case-insensitive)**. Sia `"FirstName"` sia `"firstname"` vengono associati al campo `FirstName`.
- **Comportamento della v2**: per impostazione predefinita, la corrispondenza **distingue le maiuscole dalle minuscole (case-sensitive)** e associa solo i campi che corrispondono esattamente.
- **Motivazione del cambiamento**: la corrispondenza insensibile alle maiuscole può causare comportamenti inattesi e rallentare l'elaborazione dei campi che non corrispondono. La v2 adotta come comportamento predefinito una modalità più chiara e prevedibile.
- **Opzioni correlate**: nella v2 è possibile attivare esplicitamente l'ignoranza delle maiuscole per singolo campo tramite l'opzione di tag `json:"...,case:ignore"`, oppure applicarla globalmente con l'opzione `json.MatchCaseInsensitiveNames(true)`.

### 2. Modifica del significato dell'opzione di tag `omitempty`

- **Comportamento della v1**: il campo viene omesso in base allo "stato vuoto" del valore Go. Per "stato vuoto" si intendono `false`, `0`, i puntatori o le interfacce `nil`, e gli array, le slice, le mappe e le stringhe di lunghezza zero.
- **Comportamento della v2**: il campo viene omesso in base allo "stato vuoto" del valore JSON codificato. In altre parole, viene omesso quando viene codificato come `null`, `""`, `{}` o `[]`.
- **Motivazione del cambiamento**: la definizione della v1 dipende dal sistema di tipi di Go. La v2 si basa sul sistema di tipi JSON, offrendo un comportamento più coerente. Ad esempio, nella v1 il valore `false` di un campo di tipo `bool` viene omesso, mentre nella v2 `false` è un valore JSON non vuoto e quindi non viene omesso. Nella v2 è stata aggiunta l'opzione `omitzero`, che può sostituire il comportamento della `omitempty` della v1 applicato a `0` o `false`.
- **Opzioni correlate**: per ottenere un comportamento identico a quello della v1 nella v2, si utilizza l'opzione `json.OmitEmptyWithLegacyDefinition(true)`.

### 3. Modifica del comportamento dell'opzione di tag `string`

- **Comportamento della v1**: si applica ai campi di tipo numerico, booleano e stringa. Il valore viene nuovamente codificato all'interno di una stringa JSON (ad esempio, `int(42)` diventa `"42"`). Non viene applicata ricorsivamente ai valori all'interno di tipi composti (slice, mappe e simili).
- **Comportamento della v2**: si applica **solo ai tipi numerici** ed è applicata in modo ricorsivo. Ciò significa che anche i numeri all'interno di una slice come `[]int` vengono codificati come stringhe JSON.
- **Motivazione del cambiamento**: l'uso principale dell'opzione `string` è rappresentare i numeri a 64 bit come stringhe, per evitare la perdita di precisione. Il comportamento della v1 era limitato e poco coerente. La v2 si concentra su questo uso fondamentale ed estende il comportamento in modo ricorsivo, rendendolo più utile.
- **Opzioni correlate**: l'opzione `json.StringifyWithLegacySemantics(true)` permette di imitare il comportamento della v1.

### 4. Marshaling di slice e mappe `nil`

- **Comportamento della v1**: le slice `nil` e le mappe `nil` vengono marshalate come `null`.
- **Comportamento della v2**: per impostazione predefinita, le slice `nil` vengono marshalate come `[]` (array vuoto) e le mappe `nil` come `{}` (oggetto vuoto).
- **Motivazione del cambiamento**: `nil` è un dettaglio implementativo del linguaggio Go, e non è opportuno esporlo in un formato JSON indipendente dal linguaggio. Le notazioni `[]` e `{}`, che rappresentano collezioni vuote, sono rappresentazioni più diffuse.
- **Opzioni correlate**: nella v2 è possibile ottenere il marshaling come `null`, come nella v1, tramite le opzioni `json.FormatNilSliceAsNull(true)` o `json.FormatNilMapAsNull(true)`.

### 5. Unmarshaling di array

- **Comportamento della v1**: durante l'unmarshaling in un array Go (`[N]T`), se la lunghezza dell'array JSON differisce da quella dell'array Go non viene generato alcun errore. Se l'array JSON è più corto, lo spazio rimanente viene riempito con valori zero; se è più lungo, la parte in eccesso viene scartata.
- **Comportamento della v2**: la lunghezza dell'array JSON deve **coincidere esattamente** con quella dell'array Go. In caso contrario viene generato un errore.
- **Motivazione del cambiamento**: in Go, la lunghezza di un array di dimensione fissa ha spesso un significato importante. Il comportamento della v1 può causare perdite silenziose di dati. La v2 aumenta la correttezza con regole più rigorose.
- **Opzioni correlate**: l'opzione `json.UnmarshalArrayFromAnyLength(true)` permette di imitare il comportamento della v1.

### 6. Gestione di `time.Duration`

- **Comportamento della v1**: `time.Duration` viene trattato internamente come `int64` e codificato come numero JSON in nanosecondi.
- **Comportamento della v2**: viene codificato come stringa JSON in un formato come `"1h2m3s"`, utilizzando il metodo `time.Duration.String()`.
- **Motivazione del cambiamento**: i nanosecondi numerici sono poco leggibili, mentre la rappresentazione testuale standard di `time.Duration` è più utile.
- **Opzioni correlate**: è possibile utilizzare il comportamento della v1 tramite l'opzione di tag `json:",format:nano"` oppure tramite l'opzione `json.FormatTimeWithLegacySemantics(true)`.

### 7. Gestione dell'UTF-8 non valido

- **Comportamento della v1**: durante il marshaling o l'unmarshaling, se una stringa contiene byte UTF-8 non validi, questi vengono **sostituiti silenziosamente** con il carattere di sostituzione Unicode (`\uFFFD`).
- **Comportamento della v2**: per impostazione predefinita, in presenza di UTF-8 non valido viene **restituito un errore**.
- **Motivazione del cambiamento**: serve a prevenire la corruzione silenziosa dei dati e a conformarsi allo standard JSON più rigoroso (RFC 7493).
- **Opzioni correlate**: l'opzione `jsontext.AllowInvalidUTF8(true)` permette di imitare il comportamento della v1.

### 8. Gestione dei nomi di membri duplicati in un oggetto

- **Comportamento della v1**: un oggetto JSON può contenere membri con lo stesso nome. Viene mantenuto l'ultimo valore, che sovrascrive i precedenti.
- **Comportamento della v2**: per impostazione predefinita, in presenza di nomi di membri duplicati viene **restituito un errore**.
- **Motivazione del cambiamento**: lo standard RFC 8259 non definisce il comportamento in caso di nomi duplicati, quindi le implementazioni possono comportarsi in modo diverso. Ciò può costituire una causa di vulnerabilità di sicurezza. La v2 rifiuta esplicitamente questi casi, aumentando correttezza e sicurezza.
- **Opzioni correlate**: l'opzione `jsontext.AllowDuplicateNames(true)` permette di imitare il comportamento della v1.

---

## Differenze di implementazione e di architettura

- **v1**: si basa in larga misura su `decodeState` in `decode.go` e su una macchina a stati scritta a mano in `scanner.go`. Si tratta di una struttura monolitica in cui la logica di parsing e l'analisi semantica sono fortemente accoppiate.
- **v2**: l'architettura è stata resa più modulare.
  - **`encoding/json/jsontext`**: fornisce un tokenizer JSON (`Decoder`) e un encoder (`Encoder`) di basso livello e ad alte prestazioni. Questo pacchetto si concentra esclusivamente sugli aspetti sintattici di JSON.
  - **`encoding/json/v2`**: basandosi su `jsontext`, gestisce la conversione semantica tra tipi Go e valori JSON.
  - Grazie a questa separazione, l'analisi sintattica e quella semantica sono distinte, con un miglioramento della chiarezza del codice e delle prestazioni.

---

## Nuove API e funzionalità della v2

La v2 offre un controllo molto flessibile tramite il sistema `json.Options`.

- **`json.Options`**: è un insieme di opzioni che modificano il comportamento di marshaling e unmarshaling.
- **`json.JoinOptions(...)`**: unisce più opzioni in una sola.
- **`WithMarshalers` / `WithUnmarshalers`**: una funzionalità potente che consente di iniettare la logica di serializzazione e deserializzazione per tipi specifici senza implementare le interfacce `Marshaler` o `Unmarshaler`. È particolarmente utile quando si gestiscono tipi di pacchetti esterni.
- **Nuove opzioni**: `RejectUnknownMembers`, `Deterministic(false)`, `FormatNilSliceAsNull` e altre ancora consentono un controllo del comportamento che in v1 non era possibile.

---

## Conclusioni

`encoding/json` v2 è un'implementazione moderna che, sulla base dell'esperienza maturata con la v1, migliora in modo significativo **correttezza, prestazioni e flessibilità**. Il comportamento predefinito è diventato più rigoroso, ma grazie a un sistema di `Options` articolato è possibile riprodurre integralmente tutti i comportamenti della v1, mantenendo la compatibilità con il codice esistente e adottando gradualmente i vantaggi della v2.

- Per i **nuovi progetti**, è consigliabile utilizzare la v2 come impostazione predefinita.
- Per i **progetti esistenti**, è possibile continuare a usare `jsonv1` così com'è, oppure eseguire la migrazione a `jsonv2` introducendo gradualmente le regole più rigorose della v2 tramite `DefaultOptionsV1()`.
