Marshal e Unmarshal
json.Marshal trasforma un valore Go in byte JSON. json.Unmarshal riempie un valore Go a partire da byte JSON. Gli struct tag scelgono i nomi dei campi JSON.
Email manca nel primo output per via di omitempty e di una stringa vuota. Unmarshal ha bisogno di un puntatore (&back); passare il valore stesso restituisce un InvalidUnmarshalError.
Struct tag
La sintassi del tag è json:"name,option,option".
| Tag | Effetto |
|---|---|
json:"user_id" | usa user_id come chiave |
json:"email,omitempty" | omette quando vale false, 0, "", nil, o una slice o map vuota |
json:",omitzero" (Go 1.24) | omette quando il valore è il suo zero value, o quando il suo IsZero() restituisce true |
json:"-" | non codifica né decodifica mai questo campo |
json:"-," | usa la chiave letterale - |
json:"count,string" | codifica un numero o un bool come stringa JSON ("42") |
| nessun tag | la chiave è il nome del campo Go, UserID |
La password non esce mai dalla struct, il saldo è tra virgolette, e deleted_at sparisce mentre created_at stampa il tempo zero 0001-01-01T00:00:00Z. Questa differenza è il motivo per cui è stato aggiunto omitzero: omitempty non ha mai funzionato con le struct, ed è una sorpresa di lunga data con time.Time. Su Go 1.23 e precedenti, usa un *time.Time con omitempty per ottenere lo stesso effetto.
MarshalIndent(v, prefix, indent) produce un output leggibile. Usalo per i file di configurazione e il debug; le API di solito inviano JSON compatto.
Solo campi esportati
encoding/json usa la reflection e può vedere solo i campi esportati. È il bug JSON più comune in Go:
type point struct {
x, y int // lowercase: json.Marshal(point{1, 2}) gives {}
}
Nessun errore, nessun avviso, solo {} in uscita e campi lasciati a zero in entrata. Metti la maiuscola ai campi e aggiungi i tag per le chiavi in minuscolo.
Come la decodifica abbina i campi
Quando fai l'unmarshal in una struct:
- Le chiavi vengono abbinate al nome del tag, o al nome del campo, senza distinguere maiuscole e minuscole.
{"NAME": "x"}riempie un campo con tagjson:"name". - Le chiavi senza un campo corrispondente vengono ignorate senza avvisi.
- I campi senza una chiave corrispondente mantengono il loro valore attuale. Unmarshal non li azzera, quindi decodificare in una struct che contiene già dati li unisce.
- Un tipo non corrispondente (una stringa dove la struct ha un
int) restituisce un*json.UnmarshalTypeError, ma gli altri campi vengono comunque riempiti.
Per rifiutare chiavi inattese, per esempio in una API rigorosa o in un file di configurazione con errori di battitura, usa un Decoder con DisallowUnknownFields:
Struttura sconosciuta: map[string]any
Quando non conosci la forma in anticipo, decodifica in map[string]any o in any. La corrispondenza è fissa:
| JSON | Go |
|---|---|
| oggetto | map[string]any |
| array | []any |
| stringa | string |
| numero | float64 |
| true / false | bool |
| null | nil |
Nell'output si vedono due trappole. Ogni numero è float64, quindi m["stock"].(int) fallirebbe. E gli interi sopra 2^53 perdono precisione come float64: l'id 9007199254740993 torna indietro come ...992. UseNumber evita entrambe.
Se conosci una parte della struttura, decodifica quella parte in una struct e usa json.RawMessage per il resto. Contiene i byte grezzi di un campo così puoi decodificarlo più tardi, quando ne conosci il tipo.
Slice, map, puntatori e nil
| Valore Go | JSON |
|---|---|
slice nil (var s []int) | null |
slice vuota ([]int{}) | [] |
map nil | null |
puntatore nil | null |
[]byte | una stringa base64 |
map[string]T | un oggetto, con le chiavi ordinate |
map[int]T | un oggetto con le chiavi intere come stringhe |
La differenza tra slice nil e slice vuota conta per i client di una API che si aspettano un array. Inizializza con []T{} o make([]T, 0) quando il campo deve essere [].
Usa un campo puntatore (*int, *bool) quando in input devi distinguere "assente" da "zero". Dopo l'unmarshal, un puntatore nil significa che la chiave mancava o era null; un puntatore a 0 significa che il client ha inviato 0.
Stream: Encoder e Decoder
json.Marshal e Unmarshal lavorano su slice di byte intere. Per un io.Reader o un io.Writer (un body HTTP, un file, stdin), usa json.NewDecoder e json.NewEncoder. Un Decoder può anche leggere una sequenza di valori JSON uno alla volta:
Encoder.Encode scrive un a capo finale dopo ogni valore. Per default sia Marshal sia Encoder fanno l'escape di <, > e & come \u003c, \u003e e \u0026, così il JSON si può incorporare in sicurezza nell'HTML. SetEscapeHTML(false) lo disattiva. Negli handler HTTP, json.NewDecoder(r.Body).Decode(&v) e json.NewEncoder(w).Encode(v) sono la coppia abituale.
Codifica personalizzata
Un tipo può controllare il proprio JSON implementando json.Marshaler e json.Unmarshaler. Un caso comune è un enum con iota che deve comparire come nome, non come numero:
MarshalJSON ha un receiver valore così funziona sia per i valori sia per i puntatori; UnmarshalJSON ha bisogno di un receiver puntatore perché modifica il valore. I tipi che hanno bisogno solo di una forma testuale possono implementare invece encoding.TextMarshaler (MarshalText), che li rende utilizzabili anche come chiavi di una map.
Errori comuni
- Nomi di campo in minuscolo. Vengono saltati senza avvisi.
- Passare un valore a
Unmarshal. Serve un puntatore. - Ignorare l'errore. Il JSON malformato e i tipi non corrispondenti vengono segnalati solo attraverso l'errore.
- Dare per scontato che i numeri in
map[string]anysianoint. Sonofloat64. - Aspettarsi che
omitemptyescluda una struct vuota o untime.Timea zero. Usaomitzerosu Go 1.24, oppure un puntatore. - Contare sull'ordine dei campi nelle map. Le chiavi delle map vengono ordinate in output; i campi delle struct mantengono l'ordine di dichiarazione.
Domande frequenti
Come converto una struct in JSON in Go?
Chiama json.Marshal(v), che restituisce []byte e un errore. Vengono inclusi solo i campi esportati (nomi che iniziano con la maiuscola). Usa uno struct tag come json:"name" su un campo per controllare la sua chiave JSON. Per un output indentato usa json.MarshalIndent(v, "", " ").
Perché i campi della mia struct mancano nell'output JSON?
encoding/json vede solo i campi esportati. Un campo chiamato name (in minuscolo) gli è invisibile, sia nel marshal sia nell'unmarshal. Metti la maiuscola al campo e imposta la chiave JSON con un tag come json:"name".
Cosa fa omitempty nel JSON di Go?
omitempty esclude un campo dall'output quando contiene un valore vuoto: false, 0, "", un puntatore o un'interface nil, oppure una slice o una map vuota. Non considera vuota una struct né un time.Time. Go 1.24 ha aggiunto omitzero, che omette qualsiasi valore pari allo zero value del suo tipo (o il cui metodo IsZero() restituisce true), comprese le struct e time.Time.
Come faccio il parsing di un JSON con struttura sconosciuta in Go?
Fai l'unmarshal in map[string]any (o in any). Gli oggetti diventano map[string]any, gli array []any, le stringhe string, i booleani bool e ogni numero float64. Usa le type assertion per leggere i valori, e Decoder.UseNumber se gli interi grandi devono restare esatti.