Lekcje modułu (5/5)
`encoding/json`
encoding/json to pakiet kanoniczny do serializacji struktur Go do formatu JSON i z powrotem. Znajomość jego konwencji i pułapek pozwala zaoszczędzić wiele godzin debugowania interfejsów API i plików konfiguracyjnych.
Marszałek i Unmarszałek
Dwie główne funkcje:
data, err := json.Marshal(v) // Go → []byte JSON
err = json.Unmarshal(data, &v) // JSON → Go (pass a pointer!)type User struct {
Name string
Email string
}
u := User{Name: "Ada", Email: "ada@example.com"}
b, _ := json.Marshal(u)
fmt.Println(string(b))
// {"Name":"Ada","Email":"ada@example.com"}
var back User
_ = json.Unmarshal(b, &back)Aby uzyskać wcięte dane wyjściowe (czytelne dla człowieka), użyj json.MarshalIndent(v, "", " ").
Tylko wyeksportowane pola
json.Marshal serializuje tylko wyeksportowane pola (wielkie litery). Pola prywatne są ignorowane po cichu:
type U struct {
Name string // exported → serialized
age int // private → ignored
}Tagi struktur: kontrolowanie nazwy i opcji
Tagi json:"..." kontrolują nazwę, pominięcie i typy na wyjściu:
type User struct {
Name string `json:"name"` // rename
Email string `json:"email,omitempty"` // omit if zero value
Private string `json:"-"` // never serialized
ID int `json:"id,string"` // number as string
}Najczęściej używane opcje:
,omitempty— pomija pole jeśli jest to wartość zerowa (0, "", nil, pusty plasterek/mapa).,-— nigdy nie serializowany/deserializowany.,string— zawija liczby/boole jako ciągi znaków (przydatne w przypadku identyfikatorów JS).
Dekoder/koder dla strumieni
Podczas pracy z io.Reader/io.Writer (treść HTTP, duże pliki) używaj Decoder i Encoder zamiast ładować wszystko do pamięci:
dec := json.NewDecoder(resp.Body)
var users []User
if err := dec.Decode(&users); err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
_ = enc.Encode(users)Decoder obsługuje także dec.Token() do analizy strumieniowej kawałek po kawałku i dec.DisallowUnknownFields() do odrzucania JSON z nieoczekiwanymi kluczami (przydatne do sprawdzania poprawności danych wejściowych).
Mapy ogólne i any
Jeśli nie znasz schematu, deserializuj do map[string]any:
var v map[string]any
_ = json.Unmarshal(data, &v)
fmt.Println(v["name"])Wartości będą miały typy dynamiczne (string, float64 dla liczb, []any, map[string]any, bool, nil).
Typowe błędy
- Zapominanie
&:json.Unmarshal(data, v)niczego nie wypełnia; potrzebujesz&v. - Dekodowanie struktur z polami zerowymi: aby uniknąć paniki, sprawdź
errPRZED odczytaniem pól. - Niekompatybilne typy: deserializacja liczby na ciąg znaków lub odwrotnie zwraca
*json.UnmarshalTypeError.
Ćwiczenia
Serializuj strukturę u do JSON za pomocą json.Marshal i wydrukuj wynik jako ciąg znaków.
Rozwiązanie dostępne po 3 próbach
Deserializuj `{"Name":"Ada"}` do struktury U za pomocą json.Unmarshal i wydrukuj go.
Rozwiązanie dostępne po 3 próbach
Czy pole `name string` (małe litery) struktury jest zawarte w json.Marshal?
type U struct {
name string
}
b, _ := json.Marshal(U{name: "Ada"})
fmt.Println(string(b))