Как работать с JSON в Go?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Работа с 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
}
Оптимизация и лучшие практики
- Предпочитайте структуры (
struct) для известного формата JSON. Это даёт статическую типизацию, проверку полей и лучшее понимание данных в коде. - Для повышения производительности используйте потоковые
Encoder/DecoderвместоMarshal/Unmarshalпри работе с потоками. - Используйте
json.RawMessageдля частей JSON, которые требуют conditional unmarshaling (десериализации по условию). - Обработка ошибок обязательна. Функции пакета
encoding/jsonвозвращают ошибки, которые нужно проверять (например, при несоответствии типов). - Будьте осторожны с числами. При анмаршалинге в
interface{}числа становятсяfloat64, что может привести к потере точности для больших целых. Используйте конкретные типы илиjson.Number(вjson.Decoder). - Для очень высоких требований к производительности рассмотрите альтернативные библиотеки, такие как
json-iterator/goилиeasyjson, которые предлагают более быструю, но иногда менее безопасную сериализацию.
Go предоставляет элегантный, безопасный и эффективный набор инструментов для работы с JSON, покрывающий большинство практических потребностей, от простых структур до сложных потоковых операций.