Маршалинг JSON и использование тегов структур

Мы подошли к моменту, когда приложение должно общаться с внешним миром. В React вы привыкли к JSON как к «родному» формату: JSON.parse() превращает строку в объект, а JSON.stringify() — обратно.

В Go процесс устроен иначе из-за строгой типизации. Мы не просто преобразуем данные, а сопоставляем их с конкретными структурами, используя метаданные.

JSON в Go: Marshaling и Unmarshaling

Работа с JSON строится вокруг двух процессов:

  1. Marshaling — преобразование структуры Go в формат JSON (срез байт []byte).
  2. Unmarshaling — извлечение данных из JSON в структуру Go.

Главное отличие от TypeScript: Go использует механизм рефлексии, чтобы читать структуру в рантайме. Чтобы это работало, поля структуры должны быть экспортируемыми — начинаться с заглавной буквы. Поля со строчной буквы пакет encoding/json игнорирует.

Как показано в Сравнении 1, описание модели данных в Go требует явного маппинга полей.

Теги структур (Struct tags)

В Go принято именовать публичные поля в PascalCase, а во фронтенде стандарт — camelCase. Чтобы связать их, используют Struct tags — аннотации в обратных кавычках после типа поля.

type UserProfile struct {
    ID        int    `json:"id"`
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    // Скрывает поле в JSON, если оно пустое
    Email     string `json:"email,omitempty"` 
    // Полностью исключает поле из обмена данными
    Password  string `json:"-"` 
}

Практика: преобразование байт в данные

Данные от React-приложения приходят на сервер в виде среза байт ([]byte).

package main

import (
    "encoding/json"
    "fmt"
)

type LoginRequest struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

func main() {
    // Имитируем JSON из тела HTTP-запроса
    jsonData := []byte(`{"username": "admin", "password": "123"}`)

    var req LoginRequest
    // Передаем указатель (&req), чтобы функция записала данные в переменную
    err := json.Unmarshal(jsonData, &req)
    if err != nil {
        fmt.Println("Ошибка парсинга:", err)
        return
    }

    fmt.Printf("Пользователь %s вошел в систему\n", req.Username)
}

Гибкость с json.RawMessage

Иногда структура части JSON заранее неизвестна или её нужно обработать позже. В TypeScript для этого есть any, а в Go — json.RawMessage. Это тип, который сохраняет часть JSON как «сырой» массив байт.

Это полезно в микросервисах, когда сервис-посредник передает данные дальше, не вникая в их содержимое 🧩

type Event struct {
    Type    string          `json:"type"`
    Payload json.RawMessage `json:"payload"` // Любой валидный JSON
}

Упражнение: проектирование контракта

Спроектируйте структуру для Todo-сервиса, которая будет обмениваться данными с вашим React-фронтендом.

Создайте структуру TodoItem со следующими правилами:

  1. Поле ID (int), в JSON — id.
  2. Поле Title (string), в JSON — title.
  3. Поле IsDone (bool), в JSON — completed.
  4. Поле Tags ([]string), в JSON — tags, скрывать при отсутствии данных (omitempty).
  5. Поле InternalCode (string), которое не должно попадать в JSON.

Напишите код в IDE и проверьте, как работает маршалинг при пустых полях.

Мы разобрались с «нервной системой» API — передачей данных. Чтобы эти данные начали перемещаться между сервером и клиентом, нужен транспорт.

В следующей теме мы создадим веб-сервер на стандартной библиотеке Go 1.22+ и научимся обрабатывать реальные HTTP-запросы.

Понравился урок?

Сохраните прогресс и получите персональный курс по любой теме — без форм и паролей

Продолжить в Telegram