- Простой пример
- Скрытие полей
- Пропуск пустых значений
- Несколько имен
- Комбинации тегов
- Разные кейсы использования
- Когда не стоит использовать теги
В 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{}.
Самостоятельная работа
Когда мы превращаем структуру в JSON, теги помогают управлять тем, какие поля туда попадут и как они будут называться. С их помощью можно, например, задать другое имя поля, скрыть лишние данные или не показывать пустые значения.
Задачи:
- Создайте структуру
Article
, у которой:
* есть поле Title
— это заголовок статьи, в JSON оно должно называться title
;
* есть поле Tags
— список тегов, оно должно попадать в JSON только если в нём есть данные;
* есть поле EditorComment
— комментарий редактора, которое не должно попадать в JSON вообще.
Создайте переменную со структурой, в которой заполнен только заголовок. Преобразуйте её в JSON и убедитесь, что поля
tags
иEditorComment
в результате нет.Возьмите строку
{"title":"Go","tags":["lang"]}
, превратите её обратно в структуруArticle
и выведите получившийся объект на экран.
Показать пример ответа
package main
import (
"encoding/json"
"fmt"
)
type Article struct {
Title string `json:"title"`
Tags []string `json:"tags,omitempty"`
EditorComment string `json:"-"`
}
func main() {
// 2) Сериализация без пустых и скрытых полей
a := Article{Title: "Struct tags"}
b, _ := json.Marshal(a)
fmt.Println(string(b)) // {"title":"Struct tags"}
// 3) Десериализация
var a2 Article
_ = json.Unmarshal([]byte(`{"title":"Go","tags":["lang"]}`), &a2)
fmt.Printf("%s %v\n", a2.Title, a2.Tags) // Go [lang]
}
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.