Какой интерфейс реализовать для JSON маршаллинга и анмаршаллинга в Go и почему методы должны иметь ресивер по указателю
Что проверяют
Понимаете ли вы наборы методов (method sets) в Go и то, как encoding/json решает, вызывать ли ваш метод. В формулировке есть подвох: по указателю обязан быть только UnmarshalJSON, и интервьюер ждёт, что вы это заметите. Следом обычно просят объяснить, почему MarshalJSON с ресивером по указателю иногда молча не вызывается.
Как отвечать
Интерфейсы лежат в encoding/json:
json.Marshaler: методMarshalJSON() ([]byte, error), возвращает готовый JSON;json.Unmarshaler: методUnmarshalJSON([]byte) error, заполняет получателя из байтов.
UnmarshalJSON меняет получателя, поэтому ресивер только указатель: при ресивере по значению метод заполнит копию, и результат пропадёт. Да и сам json.Unmarshal принимает только указатель, иначе вернёт InvalidUnmarshalError.
С MarshalJSON наоборот, надёжнее ресивер по значению. Метод с ресивером *T входит в набор методов *T, но не T. Если передать в json.Marshal значение, пакет вызовет ваш метод только для адресуемых значений: элемент слайса да, а значение в мапе, поле структуры, переданной по значению, или сам аргумент нет. В итоге одна и та же структура сериализуется то вашим методом, то по умолчанию. Эталон в стандартной библиотеке: time.Time объявляет MarshalJSON на значении, а UnmarshalJSON на указателе.
Что ещё показать:
- рекурсия: внутри
MarshalJSONнельзя зватьjson.Marshalна том же типе. Объявляют новый типtype plain Person: поля и теги у него те же, а методов нет. Псевдонимtype plain = Personне подходит, он сохраняет методы; null: по соглашениюUnmarshalJSONна входеnullничего не делает;- ключи мап: для них нужен
encoding.TextMarshaler.
Где спотыкаются
Говорят, что оба метода обязаны быть на указателе. Потом не могут объяснить, почему json.Marshal(person) выдал поля по умолчанию, хотя MarshalJSON написан.
Вызывают json.Marshal на самом типе внутри его же MarshalJSON и получают переполнение стека.
Возвращают из MarshalJSON невалидный JSON, например строку без кавычек через []byte(s). Пакет проверяет результат метода, и json.Marshal падает с MarshalerError.