> Как сериализовать данные в последовательность байт в Go (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: Фабрика решений

Стек: Go

> Пример ответа

Короткий ответ

В Go сериализация в байты выполняется через стандартные пакеты: encoding/json, encoding/gob, encoding/xml, encoding/binary для бинарных форматов, а также fmt.Sprintf для простых строк. Основной подход - использовать json.Marshal для структур с экспортируемыми полями или gob.Encode для потоковой бинарной сериализации. Для кастомных форматов реализуют интерфейсы Marshaler/Unmarshaler. Выбор зависит от требований к совместимости, производительности и размеру данных.

Подробное объяснение

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

  1. JSON - самый распространённый для API и конфигурации. Использует reflection, работает только с экспортируемыми полями. Поддерживает теги структур для контроля имён и пропуска полей.

  2. Gob - бинарный формат, специфичный для Go. Эффективен для внутреннего обмена между Go-сервисами. Работает через gob.NewEncoder/gob.NewDecoder, требует регистрации типов.

  3. encoding/binary - для примитивных типов и фиксированных структур. Позволяет контролировать порядок байт (little/big endian). Требует ручного управления размерами и выравниванием.

  4. Кастомная сериализация - реализация MarshalBinary()/UnmarshalBinary() или MarshalJSON()/UnmarshalJSON() для полного контроля.

Важные нюансы:

  • JSON не сохраняет типы - числа становятся float64 при десериализации в interface{}.
  • Gob сохраняет типы, но не совместим с другими языками.
  • Для больших данных используют потоковые декодеры (json.Decoder), а не Unmarshal.
  • Слайсы и мапы сериализуются рекурсивно, циклические ссылки вызывают ошибку или бесконечную рекурсию.

На практике

Для backend-разработки чаще всего используется JSON с тегами. Пример типичной структуры:

GO
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
Password string `json:"-"`
}

При работе с бинарными протоколами (например, для высоконагруженных систем) выбирают gob или encoding/binary. Для gRPC используют protobuf, но это внешняя библиотека.

Для потоковой обработки больших JSON-файлов применяют json.Decoder с циклом по Decode(&v).

Пример кода

GO
package main
import (
"bytes"
"encoding/gob"
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "Alice", Age: 30}
// JSON
jsonBytes, _ := json.Marshal(p)
fmt.Println(string(jsonBytes)) // {"Name":"Alice","Age":30}
// Gob
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
enc.Encode(p)
gobBytes := buf.Bytes()
// Decode gob
var p2 Person
dec := gob.NewDecoder(bytes.NewReader(gobBytes))
dec.Decode(&p2)
fmt.Printf("%+v\n", p2)
}

Как отвечать на собеседовании

Начни с краткого перечисления основных способов, затем уточни, какой контекст интересует интервьюера. Если спрашивают про JSON - упомяни теги, экспортируемые поля, обработку ошибок. Если про бинарные форматы - сравни gob и binary. Покажи понимание trade-off: JSON читаем, но медленнее и больше по размеру; gob быстрее, но не кроссплатформенный. Обязательно упомяни про MarshalJSON для кастомной логики. Если спросят про производительность - скажи, что для высоконагруженных систем лучше protobuf или ручная сериализация через binary.Write.

Что проверяет интервьюер

  • Знание стандартной библиотеки и её ограничений.
  • Понимание разницы между текстовыми и бинарными форматами.
  • Умение работать с тегами структур и интерфейсами Marshaler.
  • Осознание проблем с reflection и производительностью.
  • Навыки обработки ошибок при сериализации/десериализации.
  • Понимание потоковой обработки для больших данных.

Типичные ошибки

  • Сериализация неэкспортируемых полей - они молча пропускаются.
  • Игнорирование ошибок от Marshal/Encode.
  • Использование json.Unmarshal для больших файлов вместо Decoder.
  • Забывают про json:",omitempty" для пустых полей.
  • Путают gob с binary - gob требует регистрации типов, binary работает с примитивами.
  • Не учитывают, что time.Time сериализуется в RFC3339, а не в Unix timestamp.
  • При кастомной сериализации забывают про nil и пустые слайсы.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью