Avatar ·

json.Marshal(struct) возвращает пустой JSON "{}": как исправить?

Всем привет! Я новичок в Go и столкнулся со странной проблемой при работе с JSON. У меня есть структура, я заполняю её данными, пытаюсь сериализовать в JSON с помощью `json.Marshal()`, но на выходе получаю только пустые фигурные скобки `{}`. Вот мой код: ```go package main import ( "encoding/json" "fmt" ) type User struct { Name string Age int } func main() { u := User{"Alice", 30} result, err := json.Marshal(u) if err != nil { fmt.Println("Error:", err) return } fmt.Println(string(result)) // Выводит: {} } ``` Я ожидал увидеть `{"Name":"Alice","Age":30}`, но вижу только `{}`. Ошибки нет, но данные теряются. Что я делаю не так? Я проверил, что структура заполнена правильно — если вывести поля через `fmt.Println(u.Name, u.Age)`, они отображаются корректно. Проблема именно в маршалинге. Подскажите, пожалуйста, в чём дело? Я использую Go 1.21 на Windows. Заранее спасибо! <<>>

Почему json.Marshal() возвращает пустой объект?

Проблема в том, что поля вашей структуры User объявлены с неэкспортируемыми (приватными) именами. В Go поле считается экспортируемым (публичным), только если его имя начинается с заглавной буквы. Пакет encoding/json может сериализовать только экспортируемые поля — приватные поля игнорируются без ошибки.

В вашем примере поля Name и Age — с заглавных букв, поэтому они должны работать. Но если бы вы написали:

type User struct {
    name string  // приватное поле
    age  int     // приватное поле
}

— тогда json.Marshal() действительно вернул бы {}.

Однако ваша структура корректна. Есть другая распространённая причина: вы случайно используете теги JSON с опцией -, которая исключает поле из сериализации. Проверьте, нет ли у вас такого:

type User struct {
    Name string `json:"-"`
    Age  int    `json:"-"`
}

Если это так — удалите теги или замените их на правильные:

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

Полное решение с пояснениями

Вот рабочий пример, который гарантированно выведет ожидаемый JSON:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name string `json:"name"` // тег задаёт ключ в JSON
    Age  int    `json:"age"`  // если тег опущен — ключ = имя поля (Name)
}

func main() {
    u := User{Name: "Alice", Age: 30}
    result, err := json.Marshal(u)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(string(result)) // {"name":"Alice","age":30}
}

Проверка: что именно не так?

  • Проверьте экспортируемость — имена полей должны начинаться с заглавной буквы.
  • Проверьте теги JSON — если есть json:"-", поле будет пропущено.
  • Используйте json.MarshalIndent для отладки — он покажет структуру.
  • Проверьте вложенные структуры — если поле само является структурой, его поля тоже должны быть экспортируемыми.

Если после всех проверок проблема остаётся — выложите точную копию вашего кода, возможно, есть опечатка или невидимый символ.

<<>>

Альтернативный подход: используйте указатели или кастомную сериализацию

Иногда пустой JSON возникает из-за того, что поля структуры являются указателями или имеют нулевые значения (например, 0, "", nil). По умолчанию json.Marshal включает поля с нулевыми значениями, если не указан тег omitempty. Но если вы используете json:"fieldname,omitempty" и поле пустое — оно исчезнет из вывода.

Если вам нужно управлять форматом JSON вручную, реализуйте интерфейс json.Marshaler:

type User struct {
    Name string
    Age  int
}

func (u User) MarshalJSON() ([]byte, error) {
    return json.Marshal(map[string]interface{}{
        "user_name": u.Name,
        "user_age":  u.Age,
    })
}

Этот метод даёт полный контроль над выходным JSON. Также проверьте, не используете ли вы json.RawMessage или interface{} — они могут вести себя неожиданно.