Язык, на котором говорят API
API обмениваются данными в формате JSON — это текст вида {"id": 1, "name": "Ana"}. Пакет encoding/json в Go переводит в обе стороны: Marshal превращает структуру в JSON, а Unmarshal заполняет структуру из JSON.
Теги структуры связывают эти два мира: тег `json:"name"` сопоставляет экспортируемое поле Name в Go с ключом JSON в нижнем регистре. Переводятся только экспортируемые поля (те, что начинаются с заглавной буквы).
От структуры к JSON
json.Marshal выдаёт компактный JSON для машин, а json.MarshalIndent — читаемый JSON для людей и логов. Опция тега omitempty убирает поля, которые содержат нулевое значение.
Marshal: с отступами и компактно
У Ana нет email, и omitempty полностью убирает пустое поле из её JSON. Полная запись Ben выводится с отступами.
Вывод: программа на Go и захваченный вывод
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
}
func main() {
ana := User{ID: 1, Name: "Ana"}
compact, _ := json.Marshal(ana)
fmt.Println(string(compact))
ben := User{ID: 2, Name: "Ben", Email: "[email protected]"}
pretty, _ := json.MarshalIndent(ben, "", " ")
fmt.Println(string(pretty))
}
От JSON к структуре
json.Unmarshal разбирает JSON в структуру, которую вы передаёте (в виде указателя). Неизвестные ключи JSON просто игнорируются, а для отсутствующих ключей остаётся нулевое значение. Возвращаемая ошибка — единственный признак того, что JSON был некорректным, поэтому всегда проверяйте её.
Unmarshal и проигнорированный лишний ключ
В JSON есть ключ extra, которого нет в структуре, — ошибки не будет, он просто пропускается. А вот испорченный JSON возвращает настоящую ошибку.
Вывод: программа на Go и захваченный вывод
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func main() {
data := []byte(`{"id": 7, "name": "Cleo", "extra": "проигнорировано"}`)
var u User
if err := json.Unmarshal(data, &u); err != nil {
fmt.Println("некорректный JSON:", err)
return
}
fmt.Printf("%+v\n", u)
var broken User
err := json.Unmarshal([]byte(`{"id": `), &broken)
fmt.Println("ошибка:", err)
}
Вложенные данные
Реальные данные API вложены друг в друга: заказ содержит своего пользователя и список товаров. Структуры внутри структур и срезы автоматически превращаются в объекты и массивы JSON — никакой особой обработки не нужно.
Заказ с пользователем внутри
Вложенная структура User становится вложенным объектом JSON, а срез товаров — массивом JSON: форма данных в Go и есть форма JSON.
Вывод: программа на Go и захваченный вывод
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
type Order struct {
ID int `json:"id"`
Items []string `json:"items"`
User User `json:"user"`
}
func main() {
order := Order{
ID: 1042,
Items: []string{"клавиатура", "мышь"},
User: User{ID: 1, Name: "Ana"},
}
data, _ := json.MarshalIndent(order, "", " ")
fmt.Println(string(data))
}