Eu tenho uma estrutura como esta:
type Result struct {
Data MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Mas mesmo se a instância de MyStruct estiver totalmente vazia (ou seja, todos os valores são padrão), ela está sendo serializada como:
"data":{}
Eu sei que os documentos de codificação / json especificam que os campos "vazios" são:
falso, 0, qualquer ponteiro nulo ou valor de interface e qualquer matriz, fatia, mapa ou string de comprimento zero
mas sem considerar uma estrutura com todos os valores vazios / padrão. Todos os seus campos também são marcados com omitempty
, mas isso não tem efeito.
Como posso fazer com que o pacote JSON não empacote meu campo que é uma estrutura vazia?
&MyStruct{ /* values */ }
conta como um ponteiro nulo? O valor não é nulo.Como @chakrit mencionado em um comentário, você não pode chegar a este trabalho através da implementação
json.Marshaler
deMyStruct
, e implementar uma função de triagem JSON personalizado em cada struct que usa-lo pode ser muito mais trabalho. Realmente depende do seu caso de uso se vale a pena o trabalho extra ou se você está preparado para viver com estruturas vazias em seu JSON, mas aqui está o padrão que uso aplicado aResult
: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 }
Se você tem structs enormes com muitos campos, isso pode se tornar tedioso, especialmente alterar a implementação de um struct mais tarde, mas sem reescrever todo o
json
pacote para atender às suas necessidades (não é uma boa ideia), essa é praticamente a única maneira que consigo pensar em conseguir isso é feito enquanto mantém um não-ponteiroMyStruct
lá.Além disso, você não precisa usar estruturas embutidas, você pode criar estruturas nomeadas. Eu uso LiteIDE com autocompletar de código, então eu prefiro inline para evitar confusão.
fonte
Data
é uma estrutura inicializada, portanto, não é considerada vazia porqueencoding/json
olha apenas para o valor imediato, não os campos dentro da estrutura.Infelizmente, retornar
nil
dejson.Marhsler
atualmente não funciona:func (_ MyStruct) MarshalJSON() ([]byte, error) { if empty { return nil, nil // unexpected end of JSON input } // ... }
Você poderia dar
Result
um empacotador também, mas não vale a pena o esforço.A única opção, como sugere Matt, é fazer
Data
um ponteiro e definir o valor comonil
.fonte
encoding/json
que não posso verificar os campos filho da estrutura. Não seria muito eficiente, sim. Mas certamente não é impossível.json.Marshaler
entanto, isso pode ser feito caso a caso.MyStruct
está vazio , implementando umjson.Marshaler
emMyStruct
si. Prova: play.golang.org/p/UEC8A3JGvxjson.Marshaler
noResult
próprio tipo de conteúdo , o que pode ser muito inconveniente.Há uma excelente proposta da Golang para esse recurso que está ativa há mais de 4 anos, portanto, neste ponto, é seguro presumir que ele não fará parte da biblioteca padrão tão cedo. Como @Matt apontou, a abordagem tradicional é converter os structs em ponteiros para structs . Se essa abordagem for inviável (ou impraticável), uma alternativa é usar um codificador json alternativo que suporte a omissão de estruturas de valor zero .
Eu criei um espelho da biblioteca Golang json ( clarketm / json ) com suporte adicionado para omitir estruturas de valor zero quando a
omitempty
tag é aplicada. Essa biblioteca detecta o zeroness de maneira semelhante ao popular codificador YAML go-yaml , verificando recursivamente os campos de estrutura pública .por exemplo
$ 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" }
fonte