Работа с JSON в Go: парсинг и сериализация на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с JSON в Go: парсинг и сериализация на практике

JSON (JavaScript Object Notation) — один из самых популярных форматов обмена данными в веб-разработке. В языке Go (Golang) работа с JSON реализована через стандартный пакет encoding/json, который предоставляет мощные и удобные инструменты для сериализации (преобразования структур данных в JSON) и десериализации (парсинга JSON в структуры Go). В этой статье мы подробно разберём, как работать с JSON в Go на практических примерах.

Основы: сериализация структур в JSON

Самый распространённый способ работы с JSON в Go — использование структур с тегами. Теги позволяют задавать имена полей в JSON, которые могут отличаться от имён полей в Go. Рассмотрим базовый пример сериализации:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    IsActive bool   `json:"is_active"`
}

func main() {
    user := User{
        ID:       1,
        Name:     "Иван Петров",
        Email:    "ivan@example.com",
        IsActive: true,
    }

    jsonData, err := json.Marshal(user)
    if err != nil {
        fmt.Println("Ошибка сериализации:", err)
        return
    }

    fmt.Println(string(jsonData))
    // Вывод: {"id":1,"name":"Иван Петров","email":"ivan@example.com","is_active":true}
}

Обратите внимание: мы используем теги json:"имя_поля", чтобы управлять именами ключей в JSON. Это особенно удобно, когда нужно соблюдать определённые стандарты именования (например, snake_case в JSON и CamelCase в Go).

Парсинг JSON в структуры Go

Десериализация (парсинг) JSON-данных в структуры выполняется с помощью функции json.Unmarshal. Важно, чтобы структура соответствовала структуре JSON-объекта. Продолжим с нашим примером:

func main() {
    jsonString := `{"id":2,"name":"Мария Сидорова","email":"maria@example.com","is_active":false}`

    var user User
    err := json.Unmarshal([]byte(jsonString), &user)
    if err != nil {
        fmt.Println("Ошибка парсинга:", err)
        return
    }

    fmt.Printf("ID: %d, Name: %s, Email: %s, Active: %t\n",
        user.ID, user.Name, user.Email, user.IsActive)
    // Вывод: ID: 2, Name: Мария Сидорова, Email: maria@example.com, Active: false
}

Важно: В функцию json.Unmarshal всегда передаётся указатель на структуру (&user), иначе изменения не будут сохранены.

Работа с вложенными структурами и массивами

Реальные JSON-данные часто содержат вложенные объекты и массивы. Go позволяет элегантно обрабатывать такие случаи с помощью вложенных структур:

type Address struct {
    City    string `json:"city"`
    Street  string `json:"street"`
    ZipCode string `json:"zip_code"`
}

type Employee struct {
    ID      int      `json:"id"`
    Name    string   `json:"name"`
    Skills  []string `json:"skills"`
    Address Address  `json:"address"`
}

func main() {
    jsonData := `{
        "id": 101,
        "name": "Алексей Иванов",
        "skills": ["Go", "Python", "Docker"],
        "address": {
            "city": "Москва",
            "street": "Тверская, 10",
            "zip_code": "101000"
        }
    }`

    var emp Employee
    err := json.Unmarshal([]byte(jsonData), &emp)
    if err != nil {
        fmt.Println("Ошибка:", err)
        return
    }

    fmt.Printf("Сотрудник: %s, Город: %s, Навыки: %v\n",
        emp.Name, emp.Address.City, emp.Skills)
    // Вывод: Сотрудник: Алексей Иванов, Город: Москва, Навыки: [Go Python Docker]
}

Для массивов в Go используются срезы ([]string), а для вложенных объектов — отдельные структуры. При парсинге JSON-массива объектов можно использовать срез структур:

type Product struct {
    Title string  `json:"title"`
    Price float64 `json:"price"`
}

func main() {
    jsonArray := `[
        {"title":"Книга","price":500.50},
        {"title":"Ручка","price":45.00}
    ]`

    var products []Product
    json.Unmarshal([]byte(jsonArray), &products)

    for _, p := range products {
        fmt.Printf("%s - %.2f руб.\n", p.Title, p.Price)
    }
}

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

Иногда структура JSON заранее неизвестна или может меняться. В таких случаях удобно использовать map[string]interface{}. Это позволяет обрабатывать произвольные JSON-объекты:

func main() {
    jsonString := `{
        "name": "Тестовый объект",
        "version": 2.5,
        "enabled": true,
        "metadata": {
            "created": "2024-01-15",
            "author": "admin"
        }
    }`

    var data map[string]interface{}
    err := json.Unmarshal([]byte(jsonString), &data)
    if err != nil {
        fmt.Println("Ошибка:", err)
        return
    }

    // Доступ к значениям с приведением типов
    name := data["name"].(string)
    version := data["version"].(float64)
    enabled := data["enabled"].(bool)

    fmt.Printf("Имя: %s, Версия: %.1f, Активен: %t\n", name, version, enabled)

    // Доступ к вложенным данным
    metadata := data["metadata"].(map[string]interface{})
    author := metadata["author"].(string)
    fmt.Println("Автор:", author)
}

Важно: При работе с map[string]interface{} необходимо явно приводить типы, так как значения имеют тип interface{}. Числа в JSON по умолчанию парсятся как float64, а не int.

Обработка ошибок и пропущенных полей

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

  • Проверка ошибок: Всегда проверяйте err после вызова json.Unmarshal или json.Marshal.
  • Пропуск полей: Если поле отсутствует в JSON, но есть в структуре, оно остаётся с нулевым значением (0, "", false, nil).
  • Игнори

Блоги

Рекомендаций по книгам