Язык Go получил широкое распространение в бэкенд-программировании, и с каждым днем сообщество его разработчиков становится все больше.Мне тоже очень нравится писать код на Go.
Недавно я работал над проектом, в источнике данных которого имелись необязательные поля. Нужно было использовать получаемый ответ в соответствии с поставленной задачей.
Я подумал, что неплохо было бы на основе материалов проекта сделать небольшое руководство.
Сначала создадим простую структуру и посмотрим на ответ
Только не забрасывайте меня помидорами за использование uint64
для childrenCount
, это всего лишь пример.
Прежде чем выполнять сериализацию, десериализируем сущность из источника данных. Получаем соответствующий ответ:
Добавим псевдоним для использования в сериализации/десериализации json.
И ответ будет таким:
… для сериализации несуществующих значений с помощью значений по умолчанию
Подход со значениями по умолчанию устанавливает для необязательных/несуществующих полей значения по умолчанию.
Видите потенциальный источник проблем?
В источнике данных здесь есть необязательное поле childrenCount
, обозначающее количество детей у того или иного сотрудника. У одного сотрудника значение этого поля равно 0
, а у другого — null
. Мне не хотелось ставить 0
в это поле у обоих сотрудников, потому что 0
и отсутствие информации — это не одно и то же.
Когда же 0
в источнике данных использовать никак нельзя, смело ставьте null
вместо 0
.
Но ради практических целей мы пойдем дальше и все же дифференцируем 0
и null
.
… для сериализации несуществующих и «пустых» значений
Этот подход пропускает пустые значения и null
(такие как 0 или пустая строка).
В Go есть тег omitempty, который помогает не сериализовывать пустые поля.
Добавим в нашу сущность поле ownedCars
и реализуем:
Ответ не изменился, потому что поле ownedCars
не пустое.
Теперь заменим имеющееся в ownedCars
значение на null
:
И ответ станет таким:
Сериализации пустого значения не произошло!
Но поле childrenCount
тоже необязательное, поэтому добавим и в него omitempty
. На этот раз возьмем данные по другому сотруднику — Джулии, у которой нет детей.
Теперь в ответе опущены оба необязательных поля childrenCount
и ownedCars
, так как Go читает 0
как пустое значение.
Такой ответ удовлетворяет вашим задачам? Тогда останавливайтесь на этом.
Но опять же ради практических целей мы пойдем дальше, ведь значение childrenCount
оказалось потерянным. Хотя мы знаем, что оно равно 0
в источнике данных как валидное значение для данного случая.
… для сериализации «пустых» полей и недопущения сериализации несуществующих
Этот подход опускает значения null и оставляет нули и пустые строки как есть.
Вернемся к тому, какой была наша последняя структура (попрощаемся с Джейсоном и продолжим работать с Джулией):
Почему Go присваивает этим полям значения по умолчанию? Главная причина — такие типы, как uint64
или string
, не могут быть равны нулю (в отличие от, например, срезов). А вот их указатели могут. Поэтому необходимо включить в нашу сущность ссылку на указатель.
Вот так:
И теперь ответ будет таким:
Теперь он такой, как нам нужно!
Когда вам понадобится преобразовать эту сущность в другую структуру, например EmployeeDto
, снова включите ссылку на указатель и задайте поля напрямую. И все получится.
Такой ответ тоже удовлетворяет вашим задачам? Тогда останавливайтесь на этом. (Что касается моих задач, это было именно то, что нужно).
Но, как вы поняли, этот подход не сериализовывает поля со значением null
. Вам нужно выполнить явную сериализацию этих полей, чтобы в ответе были поля со значением null
? Тогда продолжим.
… для сериализации несуществующих полей со значением null
Этот подход сериализовывает значения null как значения null и оставляет «пустые» поля как есть.
До сих пор мне еще не приходилось выполнять явную сериализацию полей со значением null
. Для целей поставленных задач было достаточно просто опустить их в ответе так, чтобы было понятно, что это значения null
.
Для использования этого подхода есть библиотека guregu/null, которая активно применяется сообществом Go.
Лично я не реализовывал этот подход в продакшене, но думаю, что все должно пройти гладко.
Библиотека задействуется очень просто:
И теперь ответ будет таким:
Вот и все. Это было краткое руководство о том, как использовать метод сериализации json в соответствии с поставленной перед программистом задачей. Надеюсь, мне удалось пошагово разобрать все возможные сценарии.
Читайте также:
- Нормальное завершение работы в Go
- Основы Go: ввод-вывод файловой системы
- Использование конкурентности при создании API в Go
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Emre Tanriverdi: Json serialization of optional fields in Go