← Назад к вопросам

Как работать с JSON в Go?

1.0 Junior🔥 101 комментариев
#Основы Go#Сетевые протоколы и API

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Работа с JSON в Go: механизмы, практики и оптимизация

В языке Go работа с JSON построена на мощной стандартной библиотеке encoding/json. Это один из наиболее часто используемых пакетов, предоставляющий высокоуровневые и низкоуровневые API для сериализации (маршалинга) и десериализации (анмаршалинга) данных. Ниже я детально разберу ключевые подходы, структуры и тонкости.

Основные операции: Marshal и Unmarshal

Основные функции — json.Marshal() для преобразования структур Go в JSON (байтовый слейс), и json.Unmarshal() для обратного процесса.

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID        int    `json:"id"`
    Username  string `json:"username"`
    Email     string `json:"email,omitempty"` // omitempty исключает поле из JSON, если оно пустое
    IsActive  bool   `json:"is_active"`
}

func main() {
    // Маршалинг (сериализация)
    user := User{ID: 1, Username: "alice", Email: "", IsActive: true}
    jsonData, err := json.Marshal(user)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(jsonData)) // {"id":1,"username":"alice","is_active":true}

    // Анмаршалинг (десериализация)
    var decodedUser User
    err = json.Unmarshal(jsonData, &decodedUser)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%+v\n", decodedUser) // {ID:1 Username:alice Email: IsActive:true}
}

Аннотации (теги) структур: контроль формата JSON

Теги json: в определении полей структуры — это основной инструмент контроля.

  • json:"field_name" — задаёт имя поля в JSON.
  • json:"-" — полностью игнорирует поле при маршалинге/анмаршалинге.
  • json:"field_name,omitempty" — исключает поле из результата маршалинга, если его значение пустое (zero value).
  • json:"field_name,string" — при маршалинге преобразует числовое поле (например, int) в JSON-строку (например, "100"). Это полезно для взаимодействия со строгими системами, требующими определённого типа.

Работа с динамическими JSON: map[string]interface{} и json.RawMessage

Когда структура JSON заранее неизвестна или сильно варьируется, используют гибкие типы.

// Использование map для произвольного JSON
var dynamicData map[string]interface{}
jsonStr := `{"name": "Bob", "age": 30, "tags": ["dev", "go"]}`
err := json.Unmarshal([]byte(jsonStr), &dynamicData)
// dynamicData["age"] будет типа float64 (числа в JSON по умолчанию unmarshal в float64)

// json.RawMessage для отложенной или многократной десериализации
type Message struct {
    Header map[string]string `json:"header"`
    Body   json.RawMessage  `json:"body"` // сохраняет исходные JSON-байты
}
// Затем Body можно unmarshal в разные структуры по условию

Потоковая обработка: json.Encoder и json.Decoder

Для работы с потоками данных (например, HTTP-ответы, большие файлы) вместо полного чтения в память используют Encoder/Decoder. Они работают напрямую с io.Writer и io.Reader, что эффективно для производительности и памяти.

// Запись JSON в поток (например, в HTTP response)
func writeJSON(w http.ResponseWriter, data interface{}) error {
    encoder := json.NewEncoder(w)
    encoder.SetIndent("", "  ") // для красивого форматирования (опционально)
    return encoder.Encode(data)
}

// Чтение JSON из потока (например, из HTTP request body)
func readJSON(r io.Reader, target interface{}) error {
    decoder := json.NewDecoder(r)
    decoder.DisallowUnknownFields() // Строгий режим: ошибка при неизвестных полях
    return decoder.Decode(target)
}

Кастомная логика сериализации: интерфейсы json.Marshaler и json.Unmarshaler

Для полного контроля над процессом можно реализовать эти интерфейсы в своих типах.

type CustomDate struct {
    time.Time
}

// Задаём свой формат даты при маршалинге
func (cd CustomDate) MarshalJSON() ([]byte, error) {
    return []byte(`"` + cd.Time.Format("2006-01-02") + `"`), nil
}

// Парсим свой формат даты при анмаршалинге
func (cd *CustomDate) UnmarshalJSON(data []byte) error {
    // удаляем окружающие кавычки и парсим
    str := string(data[1 : len(data)-1])
    t, err := time.Parse("2006-01-02", str)
    if err != nil {
        return err
    }
    cd.Time = t
    return nil
}

Оптимизация и лучшие практики

  1. Предпочитайте структуры (struct) для известного формата JSON. Это даёт статическую типизацию, проверку полей и лучшее понимание данных в коде.
  2. Для повышения производительности используйте потоковые Encoder/Decoder вместо Marshal/Unmarshal при работе с потоками.
  3. Используйте json.RawMessage для частей JSON, которые требуют conditional unmarshaling (десериализации по условию).
  4. Обработка ошибок обязательна. Функции пакета encoding/json возвращают ошибки, которые нужно проверять (например, при несоответствии типов).
  5. Будьте осторожны с числами. При анмаршалинге в interface{} числа становятся float64, что может привести к потере точности для больших целых. Используйте конкретные типы или json.Numberjson.Decoder).
  6. Для очень высоких требований к производительности рассмотрите альтернативные библиотеки, такие как json-iterator/go или easyjson, которые предлагают более быструю, но иногда менее безопасную сериализацию.

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