ProgrammazioneBackend Go разработчик

Как реализована работа с JSON (encoding/json) в Go: особенности сериализации, особенности тегов struct, частые боли при маршалинге/размаршалинге и обходные пути?

Supera i colloqui con l'assistente IA Hintsage

Ответ.

В Go JSON маршалинг стандартно реализован через пакет encoding/json. Сериализовать можно только экспортируемые (с большой буквы) поля структур. Для управления именами и обработкой используют теги struct:

type User struct { ID int `json:"id"` Name string `json:"name,omitempty"` Age int `json:"age,string"` }
  • omitempty исключает поле при нулевом значении;
  • string сериализует значение как строку, даже если это число.

Пример сериализации:

user := User{ID: 1, Name: "Олег"} b, _ := json.Marshal(user) fmt.Println(string(b)) // {"id":1,"name":"Олег","age":"0"}

Пример разборки JSON

var u User json.Unmarshal([]byte('{"id":2,"name":"Иван"}'), &u)

Вопрос с подвохом.

Как сериализуются неэкспортируемые (с маленькой буквы) поля структуры при маршалинге в JSON?

Правильный ответ: Они игнорируются, в сериализации участвуют только экспортируемые (с большой буквы) поля. Это часто внезапно ломает API:

type Foo struct { bar int // не будет сериализовано! }

Примеры реальных ошибок из-за незнания тонкостей темы.


История

Бэкенд выдавал неполные JSON-объекты, т.к. часть нужных полей осталась неэкспортируемой (с маленькой буквы). Несколько дней ушло на выяснение, почему клиенты не видят эти поля.


История

В ответе API поле содержало конструкцию omitempty, но иногда приходило пустое значение, потому что нулевое значение для среза — это nil, а не пустой срез. Клиенты получали null вместо пустого массива [] и падали при парсинге.


История

В проекте подмешивали динамические поля к структурам через map[строка]интерфейс, но забывали реализовать кастомный UnmarshalJSON, из-за чего часть данных "терялась" без ошибок. Данные клиента терялись и вручную восстанавливались по резервным копиям.