Ce face encoding/json.Marshaler
Dacă un tip are metoda MarshalJSON() ([]byte, error) (implementează json.Marshaler), json.Marshal o apelează în loc să folosească regulile obișnuite. Dacă are UnmarshalJSON([]byte) error (implementează json.Unmarshaler), json.Unmarshal îi dă textul JSON brut al valorii și o lasă pe ea să-l interpreteze.
Se folosesc pentru formate speciale: o dată scrisă ca "19.10.2026", o sumă păstrată în bani și scrisă în lei, un enum scris ca text. MarshalJSON are de obicei receptor valoare (ca să funcționeze și pentru valori, nu doar pentru pointeri), iar UnmarshalJSON are receptor pointer, pentru că modifică valoarea.
Ce returnează MarshalJSON trebuie să fie JSON valid; cel mai sigur este să-l produci tot cu json.Marshal. Tipurile care implementează encoding.TextMarshaler (MarshalText) sunt și ele scrise ca text JSON.
Ce returnează
MarshalJSON: textul JSON și o eroare. UnmarshalJSON: o eroare.
Exemple
Exemplul 1: Dată în format românesc
package main
import (
"encoding/json"
"fmt"
"time"
)
type DataRo struct{ time.Time }
func (d DataRo) MarshalJSON() ([]byte, error) {
return json.Marshal(d.Format("02.01.2006"))
}
func (d *DataRo) UnmarshalJSON(b []byte) error {
var text string
if err := json.Unmarshal(b, &text); err != nil {
return err
}
t, err := time.Parse("02.01.2006", text)
if err != nil {
return fmt.Errorf("dată invalidă %q: folosește ZZ.LL.AAAA", text)
}
d.Time = t
return nil
}
type Factura struct {
Numar string `json:"numar"`
Scadenta DataRo `json:"scadenta"`
}
func main() {
f := Factura{"F-147", DataRo{time.Date(2026, 11, 18, 0, 0, 0, 0, time.UTC)}}
out, _ := json.Marshal(f)
fmt.Println(string(out))
var g Factura
err := json.Unmarshal([]byte(`{"numar":"F-148","scadenta":"25.12.2026"}`), &g)
fmt.Println(g.Numar, g.Scadenta.Weekday(), g.Scadenta.YearDay(), err)
err = json.Unmarshal([]byte(`{"numar":"F-149","scadenta":"2026-12-25"}`), &g)
fmt.Println("Eroare:", err)
}
Rezultat
{"numar":"F-147","scadenta":"18.11.2026"}
F-148 Friday 359 <nil>
Eroare: dată invalidă "2026-12-25": folosește ZZ.LL.AAAA
Exemplul 2: Bani păstrați în bani, scriși în lei
Sumele se țin ca întregi (bani), ca să nu apară erori de rotunjire, dar în JSON apar în lei.
package main
import (
"encoding/json"
"fmt"
"math"
)
type Lei int64 // în bani
func (l Lei) MarshalJSON() ([]byte, error) {
return json.Marshal(float64(l) / 100)
}
func (l *Lei) UnmarshalJSON(b []byte) error {
var v float64
if err := json.Unmarshal(b, &v); err != nil {
return err
}
*l = Lei(math.Round(v * 100))
return nil
}
type Produs struct {
Nume string `json:"nume"`
Pret Lei `json:"pret"`
}
type Cos struct {
Produs string `json:"produs"`
Bucati int `json:"bucati"`
Total Lei `json:"total"`
}
func main() {
var p Produs
if err := json.Unmarshal([]byte(`{"nume":"cafea","pret":32.99}`), &p); err != nil {
fmt.Println(err)
return
}
fmt.Println("Preț păstrat în bani:", int64(p.Pret))
c := Cos{Produs: p.Nume, Bucati: 3, Total: p.Pret * 3}
out, _ := json.Marshal(c)
fmt.Println(string(out))
}
Rezultat
Preț păstrat în bani: 3299
{"produs":"cafea","bucati":3,"total":98.97}
Greșeli frecvente
- În
MarshalJSONapelezijson.Marshal(d)pe același tip: metoda se apelează pe ea însăși la nesfârșit. Convertește întâi la alt tip (de exemplutype alias T, apoijson.Marshal(alias(d))). - Returnezi text care nu este JSON valid, de exemplu
[]byte(d.Format(...))fără ghilimele:Marshaldă eroare. Foloseștejson.Marshal(text). - Scrii
UnmarshalJSONcu receptor valoare: modificările se fac pe o copie și se pierd.
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.Unmarshal: Citește JSON și completează o valoare Go (structură, map, slice) dată prin pointer; potrivește cheile după etichetele json.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.time.Time.Format: Transformă un moment în text după un model scris cu data de referință 2 ianuarie 2006, 15:04:05.