117 votes

Comment ne pas marshaler une structure vide en JSON avec Go ?

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 (c'est-à-dire que toutes les valeurs sont par défaut), elle est sérialisée en tant que :

"data":{}

Je sais que le encodage/json Les documents précisent que les champs "vides" le sont :

false, 0, tout pointeur nil ou valeur d'interface, et tout tableau, tranche, carte ou chaîne de longueur zéro.

mais sans tenir compte d'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 paquet JSON pour pas marshal mon champ qui est un struct vide ?

181voto

Matt Points 5202

Comme le dit la documentation, "tout pointeur nul". -- faire de la structure un pointeur. Les pointeurs ont des valeurs "vides" évidentes : nil .

Correction - définir le type avec un struct pointeur champ :

type Result struct {
    Data       *MyStruct `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

Puis une valeur comme celle-ci :

result := Result{}

Maréchal en tant que :

{}

Explication : Remarquez le *MyStruct dans notre définition de type. La sérialisation JSON ne se soucie pas de savoir s'il s'agit d'un pointeur ou non - c'est un détail d'exécution. Ainsi, faire des champs struct en pointeurs n'a que des implications pour la compilation et l'exécution).

Notez simplement que si vous changez le type de champ de MyStruct a *MyStruct vous aurez besoin de pointeurs vers des valeurs de structure pour le remplir, comme suit :

Data: &MyStruct{ /* values */ }

22voto

Leylandski Points 401

Comme @chakrit l'a mentionné dans un commentaire, vous ne pouvez pas faire fonctionner ce système en implémentant json.Marshaler sur MyStruct et la mise en œuvre d'une fonction de triage JSON personnalisée pour chaque structure qui l'utilise peut représenter un travail beaucoup plus important. Cela dépend vraiment de votre cas d'utilisation pour savoir si cela vaut la peine de faire ce travail supplémentaire ou si vous êtes prêt à vivre avec des structures vides dans votre 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
}

Si vous avez d'énormes structures avec de nombreux champs, cela peut devenir fastidieux, surtout si vous changez l'implémentation d'une structure plus tard, mais à moins de réécrire l'ensemble de l'application json pour répondre à vos besoins (ce qui n'est pas une bonne idée), c'est à peu près la seule façon à laquelle je pense de faire cela tout en conservant un paquet non pointeur. MyStruct là-dedans.

De plus, vous n'êtes pas obligé d'utiliser des structs en ligne, vous pouvez en créer de nommés. Cependant, j'utilise LiteIDE avec la complétion de code, donc je préfère les structures en ligne pour éviter le désordre.

9voto

Luke Points 4282

Data est un struct initialisé, il n'est donc pas considéré comme vide car encoding/json ne regarde que la valeur immédiate, pas les champs à l'intérieur de la structure.

Malheureusement, le retour nil de json.Marshaler ne fonctionne pas actuellement :

func (_ MyStruct) MarshalJSON() ([]byte, error) {
    if empty {
        return nil, nil // unexpected end of JSON input
    }
    // ...
}

Vous pourriez donner Result un marshaler aussi, mais ça ne vaut pas la peine.

La seule option, comme le suggère Matt, est de faire Data un pointeur et définir la valeur de nil .

4voto

Travis Clarke Points 1715

Il existe un remarquable Golang proposition pour cette fonctionnalité qui est active depuis plus de 4 ans, donc à ce stade, on peut supposer qu'elle ne fera pas partie de la bibliothèque standard de sitôt. Comme @Matt l'a souligné, le traditionnel L'approche consiste à convertir le structures a pointeurs vers les structures . Si cette approche est irréalisable (ou peu pratique), une alternative est d'utiliser un autre encodeur json qui prend en charge omettre les structures à valeur nulle .

J'ai créé un miroir de la bibliothèque Golang json ( clarketm/json ) avec un support supplémentaire pour omettre les structures à valeur nulle lorsque le omitempty est appliquée. Cette bibliothèque détecte zeroness de manière similaire au populaire encodeur YAML go-yaml par en vérifiant récursivement les public struct fields .

par exemple

$ 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"
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X