encoding/json.Decoder.DisallowUnknownFields în Go

Face ca Decode să returneze eroare când JSON-ul are chei care nu există în structura țintă.

Sintaxă
func (dec *Decoder) DisallowUnknownFields()

Ce face encoding/json.Decoder.DisallowUnknownFields

Implicit, cheile JSON fără câmp corespondent sunt ignorate în tăcere. Asta ascunde greșeli: un client trimite "emial" în loc de "email", iar câmpul rămâne gol fără nicio eroare. După dec.DisallowUnknownFields(), astfel de chei produc eroarea json: unknown field "emial".

Se apelează o singură dată, înainte de Decode, și se aplică doar când ținta este o structură. Este potrivit pentru API-uri stricte și fișiere de configurare; pentru date de la servicii externe, care pot adăuga câmpuri noi oricând, lasă comportamentul implicit.

Ce returnează

Nimic; schimbă comportamentul apelurilor Decode următoare.

Exemple

Exemplul 1: Greșeală de scriere prinsă

package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

type Config struct {
	Port  int    `json:"port"`
	Email string `json:"email"`
}

func citeste(text string, strict bool) {
	dec := json.NewDecoder(strings.NewReader(text))
	if strict {
		dec.DisallowUnknownFields()
	}
	var c Config
	err := dec.Decode(&c)
	fmt.Printf("strict=%-5v %+v eroare: %v\n", strict, c, err)
}

func main() {
	text := `{"port": 8080, "emial": "admin@example.ro"}`
	citeste(text, false)
	citeste(text, true)
}

Rezultat

strict=false {Port:8080 Email:} eroare: <nil>
strict=true  {Port:8080 Email:} eroare: json: unknown field "emial"

Exemplul 2: Validare strictă pentru un API

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
)

type Transfer struct {
	Catre string  `json:"catre"`
	Suma  float64 `json:"suma"`
}

func transfer(w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()
	var t Transfer
	if err := dec.Decode(&t); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	fmt.Fprintf(w, "transfer de %.2f lei către %s", t.Suma, t.Catre)
}

func main() {
	for _, corp := range []string{`{"catre":"RO49AAAA1B31007593840000","suma":250}`, `{"catre":"RO49AAAA1B31007593840000","suma":250,"comision":0}`} {
		rec := httptest.NewRecorder()
		transfer(rec, httptest.NewRequest("POST", "/transfer", strings.NewReader(corp)))
		fmt.Println(rec.Code, strings.TrimSpace(rec.Body.String()))
	}
}

Rezultat

200 transfer de 250.00 lei către RO49AAAA1B31007593840000
400 json: unknown field "comision"

Greșeli frecvente

  • Apelezi DisallowUnknownFields după Decode: primul obiect a fost deja citit fără verificare.
  • Te aștepți să funcționeze cu json.Unmarshal: opțiunea există doar pe Decoder. Pentru date în memorie folosește json.NewDecoder(bytes.NewReader(date)).

Comenzi înrudite

  • encoding/json.NewDecoder: Creează un Decoder care citește JSON dintr-un io.Reader (corpul unei cereri, un fișier); Decode citește o valoare.
  • encoding/json.Unmarshal: Citește JSON și completează o valoare Go (structură, map, slice) dată prin pointer; potrivește cheile după etichetele json.
  • encoding/json.Decoder.More: Spune dacă mai urmează un element în lista sau obiectul citit; cu Token permite citirea element cu element a unei liste mari.