Почти любой современный API общается через JSON. Go предлагает встроенную поддержку этой задачи — достаточно подключить встроенный пакет encoding/json. Однако у этого удобства есть несколько нюансов:
- Неэкспортируемые поля (с маленькой буквы) игнорируются.
- Нулевые значения засоряют ответ, если их явно не убрать.
- Демаршалинг требует передачи указателя, иначе — паника.
В этой статье на примере структуры Car разберем, как превращать ее в JSON и обратно, а также научимся управлять этими процессами с помощью тегов.
Пример структуры Car
Вот структура, с которой будем работать:
type Car struct {
Model string
Year int `json:"manufactured"`
Electric bool `json:"electric,omitempty"`
Price float64 `json:"price,omitempty"`
Owners []string
}
Поля:
Model— название модели (строка), попадет в JSON без изменений.Year— год выпуска, в JSON превратится вmanufactured(произведена).Electric— электромобиль или нет, тегomitemptyуберёт поле при значенииfalse.Price— цена, тоже пропадёт при нулевом значении.Owners— список владельцев, попадет в JSON без изменений.
Обратите внимание: для полей Model и Owners мы не указывали JSON-теги, поэтому в JSON они выводятся с заглавной буквы — точно так же, как в Go-структуре. В реальных проектах так не делают. По код-стайлу имена полей в JSON принято писать с маленькой буквы, используя camelCase (большинство REST API) или snake_case (некоторые крупные компании). Поэтому для каждого поля стоит явно указывать тег:
type Car struct {
Model string `json:"model"`
Owners []string `json:"owners"`
}
Полный рабочий код
package main
import (
"encoding/json"
"fmt"
"log"
)
type Car struct {
Model string `json:"title"`
Year int `json:"manufactured"`
Electric bool `json:"electric,omitempty"`
Price float64 `json:"price,omitempty"`
Owners []string `json:"owners"`
}
func main() {
cars := []Car{
{Model: "Tesla Model S", Year: 2022, Electric: true, Price: 89990.00, Owners:[]string{"Elon", "Employee #1"}},
{Model: "Ford Mustang", Year: 1969, Electric: false, Price: 35000.00, Owners:[]string{"John", "Sarah"}},
{Model: "Toyota Camry", Year: 2020, Electric: false, Owners:[]string{"Family"}},
{Model: "Rivian R1T", Year: 2023, Electric: true, Price: 73000.00, Owners:[]string{"Adventure Co"}},
}
// Маршалинг с отступами
data, err := json.MarshalIndent(cars, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Println("Полный JSON:")
fmt.Printf("%s\n\n", data)
// Демаршалинг - извлекаем только модели машин
var models []struct{ Model string }
if err := json.Unmarshal(data, &models); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println("Список моделей:")
fmt.Println(models)
}
Что выведет программа
Полный JSON:
[
{
"Model": "Tesla Model S",
"manufactured": 2022,
"electric": true,
"price": 89990,
"Owners": [
"Elon",
"Employee #1"
]
},
{
"Model": "Ford Mustang",
"manufactured": 1969,
"price": 35000,
"Owners": [
"John",
"Sarah"
]
},
{
"Model": "Toyota Camry",
"manufactured": 2020,
"Owners": [
"Family"
]
},
{
"Model": "Rivian R1T",
"manufactured": 2023,
"electric": true,
"price": 73000,
"Owners": [
"Adventure Co"
]
}
]
Список моделей:
[{Tesla Model S} {Ford Mustang} {Toyota Camry} {Rivian R1T}]
Обратите внимание: y Toyota Camry отсутствуют поля electric и price — их значения нулевые, а тег omitempty исключил их из вывода. У Ford Mustang поле electric тоже отсутствует, потому что Electric: false — нулевое значение.
Маршалинг: как превратить структуру в JSON
Маршалинг выполняет функция json.Marshal. Она принимает Go-значение и возвращает JSON в виде среза байт.
data, err := json.Marshal(cars)
В примере используется json.MarshalIndent — та же функция, но с форматированием:
data, err := json.MarshalIndent(cars, "", " ")
Второй параметр — префикс (пустая строка). Третий параметр — отступ для каждого уровня вложенности (два пробела).
Когда что использовать:
Marshal— для передачи по сети, записи в базу данных.MarshalIndent— для логов, отладки, конфигурационных файлов.
Теги полей: управляем JSON
Теги — это метаинформация, которая записывается после типа поля в обратных кавычках.
Основной синтаксис
Field Type `json:"имя_в_json"`
Опции тегов
| Опция | Действие |
omitempty |
Не включать поле, если значение нулевое. |
- (прочерк) |
Полностью игнорировать поле при маршалинге. |
string |
Преобразовать число в строку (редко нужно) |
Примеры
type Car struct {
Model string `json:"model"` // переименовывание
VIN string `json:"-"` // не попадает в JSON
Price float64 `json:"price,omitempty" // игнорируется, если 0
}
Демаршалинг: как превратить JSON обратно в структуру
Демаршалинг выполняет функция json.Unmarshal:
var models []struct{ Model string }
if err := json.Unmarshal(data, &models); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
Важные моменты:
- Второй аргумент — указатель.
&models, а не простоmodels. Иначе изменения не сохраняются. - Частичный демаршалинг. Объявили анонимную структуру только с полем
Model. Остальные поля из JSON просто игнорируются. Это удобно, когда нужна только часть данных. - Слайс структур. JSON-массив превращается в слайс.
Частые ошибки и как их избежать
Ошибка 1. Забыли & в демаршалинге
// Плохо - данные не будут сохранены
var models []struct{ Model string }
json.Unmarshal(data, models)
// Хорошо
json.Unmarshal(data, &models)
Ошибка 2. Поля с маленькой буквы
type Car struct {
model string // не попадет в JSON
year int // не попадет в JSON
}
Ошибка 3. Неправильный синтаксис тега
// Плохо (пробел не нужен) Price float64 `json: "price,omitempty"` // Хорошо Price float64 `json:"price,omitempty"` }
Ошибка 4. Ожидание, что omitempty уберет nil из слайса
Owners []string `json:"owners,omitempty"`
Слайс nil будет исключён. Но пустой слайс (длина 0) — не nil, он попадет в JSON как [].
Ключевой вывод
Стандартный пакет encoding/json покрывает почти все задачи по работе с JSON в Go. Теги дают тонкую настройку, omitempty помогает не засорят ответ пустыми полями, а частичный демаршалинг экономит время при работе с большими API.
Был ли у вас случай, когда обработка JSON вела себя неожиданно? Может быть, вы долго искали ошибку в тегах или забыли про указатель в
Unmarshal? Делитесь в комментариях — это поможет другим не наступать на одни и те же грабли.