Ce face encoding/json.NewDecoder
json.NewDecoder(r) creează un *json.Decoder care citește JSON direct dintr-un flux: r.Body la server, resp.Body la client, un fișier deschis. dec.Decode(&v) citește o valoare JSON și o pune în v, cu aceleași reguli ca Unmarshal (pointer, etichete, chei ignorate).
Pentru că citește din flux, poate primi mai multe valori una după alta: fiecare Decode o ia pe următoare, iar la sfârșit returnează io.EOF. Așa citești fișiere JSON Lines (câte un obiect pe rând) sau răspunsuri foarte mari fără să le ții întregi în memorie.
Diferența față de Unmarshal: Decode se oprește după prima valoare și nu verifică ce urmează. Dacă vrei să refuzi un corp cu date în plus, verifică după decodare că dec.More() este false sau că un nou Decode dă io.EOF.
Parametri
| Parametru | Descriere |
|---|---|
r |
Sursa: orice io.Reader. |
Ce returnează
NewDecoder: un *json.Decoder. Decode: nil, io.EOF la sfârșitul fluxului, sau eroarea de citire.
Exemple
Exemplul 1: Corpul JSON al unei cereri
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
)
type Inscriere struct {
Nume string `json:"nume"`
Email string `json:"email"`
Curs string `json:"curs"`
}
func inscrie(w http.ResponseWriter, r *http.Request) {
var in Inscriere
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
http.Error(w, "JSON invalid: "+err.Error(), http.StatusBadRequest)
return
}
fmt.Fprintf(w, "%s (%s) s-a înscris la %s", in.Nume, in.Email, in.Curs)
}
func main() {
for _, corp := range []string{
`{"nume":"Ana","email":"ana@example.ro","curs":"Go pentru începători"}`,
`{"nume":"Ion","email":}`,
} {
rec := httptest.NewRecorder()
inscrie(rec, httptest.NewRequest("POST", "/inscriere", strings.NewReader(corp)))
fmt.Println(rec.Code, strings.TrimSpace(rec.Body.String()))
}
}
Rezultat
200 Ana (ana@example.ro) s-a înscris la Go pentru începători
400 JSON invalid: invalid character '}' looking for beginning of value
Exemplul 2: Mai multe valori dintr-un flux (JSON Lines)
Fiecare Decode citește următorul obiect; bucla se oprește la io.EOF.
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"strings"
)
type Vanzare struct {
Produs string `json:"produs"`
Suma float64 `json:"suma"`
}
func main() {
jurnal := `{"produs":"cafea","suma":65}
{"produs":"ceai","suma":14}
{"produs":"cafea","suma":32.5}
`
dec := json.NewDecoder(strings.NewReader(jurnal))
total := map[string]float64{}
for {
var v Vanzare
err := dec.Decode(&v)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
fmt.Println("eroare:", err)
return
}
total[v.Produs] += v.Suma
}
fmt.Println(total)
}
Rezultat
map[cafea:97.5 ceai:14]
Greșeli frecvente
- Citești tot corpul cu
io.ReadAllși apoi faciUnmarshal, fără limită de mărime. Pe server pune întâihttp.MaxBytesReader, apoi foloseșteDecoder. - Te aștepți ca
Decodesă dea eroare pentru{"a":1} gunoi: citește prima valoare și se oprește. Verifică restul cudec.More().
Comenzi înrudite
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.encoding/json.Decoder.DisallowUnknownFields: Face ca Decode să returneze eroare când JSON-ul are chei care nu există în structura țintă.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.NewEncoder: Creează un Encoder care scrie JSON direct într-un io.Writer (răspuns HTTP, fișier, ecran); Encode scrie o valoare.net/http.MaxBytesReader: Limitează mărimea corpului unei cereri: citirea peste n octeți returnează eroare *http.MaxBytesError.