J'ai une structure comme celle-ci:
type Result struct {
Data MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Mais même si l'instance de MyStruct est entièrement vide (ce qui signifie que toutes les valeurs sont par défaut), elle est sérialisée en tant que:
"data":{}
Je sais que les documents encoding/json spécifient que les champs "vides" sont:
false, 0, tout pointeur nul ou valeur d'interface, et tout tableau, tranche, carte ou chaîne de longueur zéro
mais sans considération pour une structure avec toutes les valeurs vides/par défaut. Tous ses champs sont également marqués avec omitempty
, mais cela n'a aucun effet.
Comment puis-je obtenir le package JSON pour pas marshaler mon champ qui est une structure vide?
Oh! Solution facile: "tout pointeur nul". - faire de la structure un pointeur.
Réparer:
type Result struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Remarquez le *MyStruct
- quand je crée un MyStruct
maintenant, je le fais simplement par référence:
myStruct := &MyStruct{ /* values */ }
Et maintenant, le MyStruct
"vide" n'est plus marshalé en JSON comme souhaité.
Comme @chakrit l'a mentionné dans un commentaire, vous ne pouvez pas faire fonctionner cela en implémentant json.Marshaler
on MyStruct
, et l'implémentation d'une fonction de marshalling JSON personnalisée sur chaque structure qui l'utilise peut être beaucoup plus compliquée. Cela dépend vraiment de votre cas d'utilisation pour savoir si cela vaut le travail supplémentaire ou si vous êtes prêt à vivre avec des structures vides dans votre JSON, mais voici le modèle que j'utilise appliqué à 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
}
Si vous avez d'énormes structures avec de nombreux champs, cela peut devenir fastidieux, en particulier la modification de l'implémentation d'une structure plus tard, mais à court de réécriture de l'ensemble du package json
pour répondre à vos besoins (ce n'est pas une bonne idée), c'est à peu près le seul façon dont je peux penser à faire cela tout en gardant un non-pointeur MyStruct
là-dedans.
De plus, vous n'avez pas à utiliser de structures en ligne, vous pouvez en créer des nommées. Cependant, j'utilise LiteIDE avec l'achèvement du code, donc je préfère en ligne pour éviter l'encombrement.
Data
est une structure initialisée, elle n'est donc pas considérée comme vide car encoding/json
ne regarde que la valeur immédiate, pas les champs à l'intérieur de la structure.
Malheureusement retournant nil
de json.Marhsler
ne fonctionne pas actuellement:
func (_ MyStruct) MarshalJSON() ([]byte, error) {
if empty {
return nil, nil // unexpected end of JSON input
}
// ...
}
Vous pouvez également donner à Result
un marshaleur, mais cela ne vaut pas la peine.
La seule option, comme le suggère Matt, est de faire de Data
un pointeur et de définir la valeur sur nil
.