Подтвердить что ты не робот

Как не маршалировать пустую структуру в 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 не маршалировать мое поле, которое является пустой структурой?

4b9b3361

Ответ 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"
}