Как не маршалировать пустую структуру в JSON с Go?
У меня есть такая структура:
type Result struct {
Data MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Но даже если экземпляр MyStruct полностью пуст (что означает, что все значения по умолчанию), он сериализуется как:
"data":{}
Я знаю, что encoding/json docs указывают, что "пустые" поля:
false, 0, любое значение nil или значение интерфейса и любой массив, срез, карта или строка нулевой длины
но без учета структуры со всеми пустыми/значениями по умолчанию. Все его поля также помечены omitempty
, но это не влияет.
Как я могу заставить пакет JSON не маршалировать мое поле, которое является пустой структурой?
Ответы
Ответ 1
О! Легкое исправление: "любой указатель нуля". - сделать struct указателем.
Fix:
type Result struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Обратите внимание на *MyStruct
- когда я создаю MyStruct
сейчас, я просто делаю это по ссылке:
myStruct := &MyStruct{ /* values */ }
И теперь "пустой" MyStruct
больше не переводится в JSON по желанию.
Ответ 2
Как упоминалось в комментарии @chakrit, вы не можете заставить это работать, реализуя json.Marshaler
on MyStruct
, а реализация пользовательской функции маршаллинга JSON для каждой используемой структуры может быть намного более эффективной. Это зависит от вашего варианта использования в отношении того, стоит ли вам дополнительная работа или готовы ли вы жить с пустыми структурами в JSON, но здесь шаблон, который я использую, применяется к Result
:
type Result struct {
Data MyStruct
Status string
Reason string
}
func (r Result) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}{
Data: &r.Data,
Status: r.Status,
Reason: r.Reason,
})
}
func (r *Result) UnmarshalJSON(b []byte) error {
decoded := new(struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
})
err := json.Unmarshal(b, decoded)
if err == nil {
r.Data = decoded.Data
r.Status = decoded.Status
r.Reason = decoded.Reason
}
return err
}
Если у вас есть огромные структуры со многими полями, это может стать утомительным, особенно изменив реализацию структуры позже, но не переписав весь пакет json
в соответствии с вашими потребностями (не очень хорошая идея), это в значительной степени единственный Я могу думать о том, чтобы сделать это, сохраняя при этом не указатель MyStruct
.
Кроме того, вам не нужно использовать встроенные структуры, вы можете создавать именованные. Я использую LiteIDE с завершением кода, хотя, поэтому я предпочитаю inline, чтобы избежать беспорядка.
Ответ 3
Data
- это инициализированная структура, поэтому она не считается пустой, потому что encoding/json
рассматривает только непосредственное значение, а не поля внутри структуры.
К сожалению, возвращение nil
из json.Marhsler
в настоящее время не работает:
func (_ MyStruct) MarshalJSON() ([]byte, error) {
if empty {
return nil, nil // unexpected end of JSON input
}
// ...
}
Вы могли бы дать Result
маршалисту, но это не стоило усилий.
Единственный вариант, по словам Мэтта, состоит в том, чтобы сделать Data
указатель и установить значение nil
.
Ответ 4
Существует выдающееся предложение Gaang proposal для этой функции, которое действует уже более 4 лет, поэтому на данный момент можно с уверенностью предположить, что оно не попадет в стандартную библиотеку в ближайшее время. Как указал @Matt, традиционный подход заключается в преобразовании структур в указатели на структуры. Если этот подход невозможен (или нецелесообразен), то альтернативой является использование альтернативного датчика json, который поддерживает пропуск структур с нулевым значением.
Я создал зеркало библиотеки Golang json (clarketm/json) с добавленной поддержкой исключения структур нулевого значения при применении тега omitempty
. Эта библиотека обнаруживает нулевое значение аналогично популярному кодеру YAML go-yaml, рекурсивно проверяя открытые поля структуры.
например,
$ go get -u "github.com/clarketm/json"
import (
"fmt"
"github.com/clarketm/json" // drop-in replacement for 'encoding/json'
)
type Result struct {
Data MyStruct 'json:"data,omitempty"'
Status string 'json:"status,omitempty"'
Reason string 'json:"reason,omitempty"'
}
j, _ := json.Marshal(&Result{
Status: "204",
Reason: "No Content",
})
fmt.Println(string(j))
// Note: 'data' is omitted from the resultant json.
{
"status": "204"
"reason": "No Content"
}