Menu

JSON in Golang: Marshal, Unmarshal e struct tag

Come codificare e decodificare JSON in Go con encoding/json: Marshal e Unmarshal, struct tag come omitempty e omitzero, output indentato, decodifica in map[string]any, rifiuto dei campi sconosciuti e streaming con Decoder.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

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".

TagEffetto
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 tagla 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 tag json:"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:

JSONGo
oggettomap[string]any
array[]any
stringastring
numerofloat64
true / falsebool
nullnil

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 GoJSON
slice nil (var s []int)null
slice vuota ([]int{})[]
map nilnull
puntatore nilnull
[]byteuna stringa base64
map[string]Tun oggetto, con le chiavi ordinate
map[int]Tun 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]any siano int. Sono float64.
  • Aspettarsi che omitempty escluda una struct vuota o un time.Time a zero. Usa omitzero su 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.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA