Структуры в Go

Теория: Теги и работа с JSON

В Go структура — это набор полей. Когда мы передаем данные наружу (например, в API в формате JSON), часто сталкиваемся с тем, что названия полей в Go и в JSON отличаются. В Go принято использовать CamelCase, а в JSON — snake_case или просто маленькие буквы. Кроме того, иногда нам нужно скрыть часть информации (например, пароль), а иногда — не отправлять пустые значения, чтобы не засорять ответ.

Решение этих задач — теги структур.

Тег — это строка в обратных кавычках, которая задает правила преобразования. Обычно он выглядит так:

Поле Тип `json:"имя_ключа,опции"`

Простой пример

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

Если не использовать теги:

u := User{Name: "Иван", Age: 30}
b, _ := json.Marshal(u)
fmt.Println(string(b))
// {"Name":"Иван","Age":30}

С тегами:

u := User{Name: "Иван", Age: 30}
b, _ := json.Marshal(u)
fmt.Println(string(b)) // {"name":"Иван","age":30}

Вывод: благодаря тегам мы получили JSON с привычными ключами, а не с Go-шными названиями полей.

Скрытие полей

Частая задача — не показывать пароли, токены и служебную информацию. Для этого используют json:"-".

type User struct {
	Name string `json:"name"`
	Password string `json:"-"`
}

u := User{Name: "Иван", Password: "12345"}
b, _ := json.Marshal(u)

fmt.Println(string(b))
// {"name":"Иван"}

Вывод: тег - гарантирует, что поле не попадет в JSON. Это защита от случайной утечки данных.

Пропуск пустых значений

Опция omitempty убирает из JSON поля с нулевым значением.

type Product struct {
	Name  string  `json:"name"`
	Price float64 `json:"price,omitempty"`
}

p := Product{Name: "Телефон"}
b, _ := json.Marshal(p)

fmt.Println(string(b))
// {"name":"Телефон"}

Здесь price не попал в JSON, потому что у float64 нулевое значение 0.0.

Вывод: omitempty экономит трафик и делает JSON чище. Особенно полезно в API.

Несколько имен

Go умеет читать данные даже с другими ключами, если поле экспортируемое. Но если JSON приходит с совсем другим названием, без тегов не обойтись.

type User struct {
	Name string `json:"username"`
	Age  int    `json:"years"`
}

jsonStr := `{"username":"Мария","years":25}`

var u User
json.Unmarshal([]byte(jsonStr), &u)

fmt.Println(u)
// {Мария 25}

Если ключи не совпадут:

jsonStr := `{"name":"Мария","age":25}`

json.Unmarshal([]byte(jsonStr), &u)

fmt.Println(u)
// { 0}

Вывод: всегда сверяйтесь с документацией API и прописывайте правильные теги.

Комбинации тегов

Можно задавать несколько правил сразу:

type Order struct {
	ID       int     `json:"id"`
	Customer string  `json:"customer"`
	Amount   float64 `json:"amount,omitempty"`
	Secret   string  `json:"-"` // не попадет в JSON
}

Вывод: через теги мы полностью контролируем, что уйдет наружу, а что останется внутри программы.

Разные кейсы использования

Работа с API

Представьте, что ваш сервис отправляет данные во внешний API, который требует ключи в snake_case.

type Product struct {
	ProductID int `json:"product_id"`
	Title string `json:"title"`
	Price float64 `json:"price"`
}

p := Product{ProductID: 101, Title: "Наушники", Price: 2999.99}
b, _ := json.Marshal(p)

fmt.Println(string(b))
// {"product_id":101,"title":"Наушники","price":2999.99}

Внутренние структуры и приватные данные

Представим, что мы храним токен авторизации, но не хотим сериализовать его:

type Session struct {
	UserID int    `json:"user_id"`
	Token  string `json:"-"` // не включать в JSON
}

Пример:

s := Session{UserID: 42, Token: "secret"}
data, _ := json.Marshal(s)
fmt.Println(string(data)) // {"user_id":42}

Вывод: теги помогают разграничивать внутреннее и внешнее представление данных.

Оптимизация ответов

Если у нас много опциональных полей (например, адрес, отчество, промокод), лучше использовать omitempty. Так JSON будет меньше и чище.

Когда не стоит использовать теги

Иногда структура с тегами — это «жесткая схема». Если внешний API часто меняется, а ключи приходят разные, может быть проще использовать map[string]interface{}:

var data map[string]interface{}

json.Unmarshal([]byte(`{"x":1,"y":"тест"}`), &data)

fmt.Println(data)
// map[x:1 y:тест]

Минус такого подхода — теряется типизация, и придется вручную проверять типы.

Вывод:

  • Для стабильных API — лучше использовать структуры с тегами.
  • Для нестабильных или динамических данных — map или interface{}.

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845