encoding/json.Unmarshal în Go

Citește JSON și completează o valoare Go (structură, map, slice) dată prin pointer; potrivește cheile după etichetele json.

Sintaxă
func Unmarshal(data []byte, v any) error

Ce face encoding/json.Unmarshal

json.Unmarshal(date, &v) citește textul JSON din date și pune valorile în v. Al doilea argument trebuie să fie un pointer (&client), altfel funcția nu are unde scrie și returnează eroare.

Într-o structură, fiecare cheie JSON este potrivită cu câmpul care are aceeași etichetă (`json:"nume"`) sau același nume, fără să țină cont de litere mari și mici. Cheile din JSON fără câmp corespondent sunt ignorate, iar câmpurile fără cheie în JSON își păstrează valoarea (zero, sau ce aveau înainte). Dacă vrei eroare pentru chei necunoscute, folosește un Decoder cu DisallowUnknownFields.

Fără structură, JSON-ul se poate citi în map[string]any sau any: obiectele devin map[string]any, listele []any, textele string, true/false devin bool, null devine nil, iar toate numerele devin float64. Tipurile greșite (text în loc de număr) și JSON-ul invalid produc erori care spun câmpul sau poziția.

Parametri

Parametru Descriere
data Textul JSON ca []byte (din fișier, din corpul unei cereri; un string se convertește cu []byte(s)).
v Pointer la variabila care primește datele.

Ce returnează

nil la succes, altfel eroarea: *json.SyntaxError (JSON invalid), *json.UnmarshalTypeError (tip greșit) sau *json.InvalidUnmarshalError (nu ai dat pointer).

Exemple

Exemplul 1: Răspunsul unui API citit într-o structură

Structurile imbricate și listele se completează singure. Cheia "versiune_api" nu are câmp și este ignorată; NUME se potrivește cu nume.

package main

import (
	"encoding/json"
	"fmt"
)

type Adresa struct {
	Oras  string `json:"oras"`
	Judet string `json:"judet"`
}

type Client struct {
	ID       int      `json:"id"`
	Nume     string   `json:"nume"`
	Adresa   Adresa   `json:"adresa"`
	Comenzi  []int    `json:"comenzi"`
	Sold     float64  `json:"sold"`
	Activ    bool     `json:"activ"`
}

func main() {
	date := []byte(`{
		"id": 42,
		"NUME": "Ion Ionescu",
		"adresa": {"oras": "Iași", "judet": "IS"},
		"comenzi": [1001, 1007, 1012],
		"sold": 149.90,
		"activ": true,
		"versiune_api": "2.1"
	}`)

	var c Client
	if err := json.Unmarshal(date, &c); err != nil {
		fmt.Println("eroare:", err)
		return
	}
	fmt.Println("Nume:   ", c.Nume)
	fmt.Println("Oraș:   ", c.Adresa.Oras, "-", c.Adresa.Judet)
	fmt.Printf("Comenzi: %v (%d)\n", c.Comenzi, len(c.Comenzi))
	fmt.Printf("Sold:    %.2f lei\n", c.Sold)
	fmt.Printf("Tot:     %+v\n", c)
}

Rezultat

Nume:    Ion Ionescu
Oraș:    Iași - IS
Comenzi: [1001 1007 1012] (3)
Sold:    149.90 lei
Tot:     {ID:42 Nume:Ion Ionescu Adresa:{Oras:Iași Judet:IS} Comenzi:[1001 1007 1012] Sold:149.9 Activ:true}

Exemplul 2: Listă de obiecte și JSON necunoscut în map

În map[string]any numerele sunt float64; pentru a le folosi ca întregi trebuie convertite.

package main

import (
	"encoding/json"
	"fmt"
)

type Produs struct {
	Nume string  `json:"nume"`
	Pret float64 `json:"pret"`
}

func main() {
	var produse []Produs
	err := json.Unmarshal([]byte(`[{"nume":"cafea","pret":32.5},{"nume":"ceai","pret":14}]`), &produse)
	fmt.Println(produse, err)

	var oarecare map[string]any
	json.Unmarshal([]byte(`{"oras":"Cluj","locuitori":286598,"metrou":false,"zone":["centru","Mărăști"],"primar":null}`), &oarecare)
	for _, k := range []string{"oras", "locuitori", "metrou", "zone", "primar"} {
		fmt.Printf("%-9s %-22s %T\n", k, fmt.Sprint(oarecare[k]), oarecare[k])
	}
	locuitori := int(oarecare["locuitori"].(float64))
	fmt.Println("Locuitori + 1:", locuitori+1)
}

Rezultat

[{cafea 32.5} {ceai 14}] <nil>
oras      Cluj                   string
locuitori 286598                 float64
metrou    false                  bool
zone      [centru Mărăști]       []interface {}
primar    <nil>                  <nil>
Locuitori + 1: 286599

Exemplul 3: Erori obișnuite

package main

import (
	"encoding/json"
	"fmt"
)

type Nota struct {
	Elev string `json:"elev"`
	Nota int    `json:"nota"`
}

func main() {
	var n Nota
	fmt.Println("1:", json.Unmarshal([]byte(`{"elev":"Ana","nota":"zece"}`), &n))
	fmt.Println("2:", json.Unmarshal([]byte(`{"elev":"Ana","nota":9,}`), &n))
	fmt.Println("3:", json.Unmarshal([]byte(`[9, 10]`), &n))
	fmt.Println("4:", json.Unmarshal([]byte(`{"elev":"Ana","nota":9.5}`), &n))

	err := json.Unmarshal([]byte(`{"elev":"Radu","nota":10}`), &n)
	fmt.Println("5:", err, n)
}

Rezultat

1: json: cannot unmarshal string into Go struct field Nota.nota of type int
2: invalid character '}' looking for beginning of object key string
3: json: cannot unmarshal array into Go value of type main.Nota
4: json: cannot unmarshal number 9.5 into Go struct field Nota.nota of type int
5: <nil> {Radu 10}

Greșeli frecvente

  • Dai valoarea în loc de pointer: json.Unmarshal(date, c). go vet raportează call of Unmarshal passes non-pointer as second argument, iar la rulare funcția returnează eroarea json: Unmarshal(non-pointer main.Client); scrie &c.
  • Câmpurile structurii sunt cu literă mică: nu pot fi completate și rămân goale fără nicio eroare.
  • Refolosești aceeași variabilă pentru mai multe documente: câmpurile care lipsesc din al doilea JSON păstrează valorile din primul. Declară o variabilă nouă pentru fiecare.
  • Faci m["id"].(int) pe un map[string]any: numerele sunt float64 și programul se oprește cu panică.

Comenzi înrudite

  • encoding/json.Marshal: Transformă o valoare Go (structură, map, slice) în JSON compact; etichetele json:”…” aleg numele și regulile câmpurilor.
  • 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.Decoder.DisallowUnknownFields: Face ca Decode să returneze eroare când JSON-ul are chei care nu există în structura țintă.
  • encoding/json.Valid: Verifică doar dacă textul este JSON valid, fără să-l citească într-o variabilă.
  • encoding/json.RawMessage: Un fragment JSON păstrat ca text brut: decodarea lui se amână (ex. după un câmp „tip”) sau se trimite mai departe neschimbat.