2013-08-06 16 views
37

Ho una struct come questo:Come eseguire il marshalling di una struttura vuota in JSON con Go?

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

Ma anche se l'istanza di MyStruct è completamente vuota (significato, tutti i valori sono di default), è in fase di serializzato come:

"data":{} 

So che le encoding/json documenti specificano che i campi "vuoti" sono:

falso, 0, qualsiasi puntatore nullo o il valore dell'interfaccia, e qualsiasi array, fetta, carta, o str di lunghezza zero

ma senza considerazione per una struttura con tutti i valori vuoti/predefiniti. Tutti i suoi campi sono anche contrassegnati con omitempty, ma questo non ha alcun effetto.

Come posso ottenere il pacchetto JSON su non marshal il mio campo che è una struttura vuota?

risposta

64

Oh! Correzione facile: "qualsiasi puntatore nullo". - rendere la struttura un puntatore.

Fix:

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

Avviso del *MyStruct - quando creo un MyStruct ora, ho semplicemente faccio per riferimento:

myStruct := &MyStruct{ /* values */ } 

E ora il "vuoto" MyStruct non è più marshaled in JSON come desiderato.

4

Data è una struttura inizializzata, quindi non è considerata vuota poiché encoding/json esamina solo il valore immediato, non i campi all'interno della struttura.

Purtroppo ritorno nil da json.Marhsler attualmente non funziona:

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

Si potrebbe dare un Result marshaler pure, ma non è valsa la pena.

L'unica opzione, come suggerisce Matt, è di rendere Data un puntatore e impostare il valore su nil.

+1

Non vedo perché 'encoding/json' ** non può ** controllare i campi secondari della struct. Non sarebbe molto efficiente, sì. Ma non è certamente impossibile. – nemo

+0

@nemo Vedo il tuo punto, ho cambiato il testo. Non lo fa perché non sarebbe efficiente. Tuttavia, può essere eseguito con 'json.Marshaler' caso per caso. – Luke

+2

Non è ** possibile ** decodificare se 'MyStruct' è vuoto_ implementando un' json.Marshaler' su 'MyStruct' stesso. Dimostrazione: http://play.golang.org/p/UEC8A3JGvx – chakrit

5

Come @chakrit menzionato in un commento, non è possibile farlo funzionare implementando json.Marshaler su MyStruct e implementando una funzione di marshalling JSON personalizzata su ogni struttura che lo utilizza può essere molto più lavoro. In realtà dipende il vostro caso d'uso per se vale la pena il lavoro supplementare o se siete disposti a vivere con le strutture vuote nel vostro JSON, ma ecco il modello che uso applicata a 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 
} 

Se si dispone di enormi strutture con molti campi questo può diventare noioso, soprattutto cambiando l'implementazione di una struct in seguito, ma a meno di riscrivere l'intero pacchetto json in base alle proprie esigenze (non una buona idea), questo è praticamente l'unico modo in cui posso pensare di ottenere questo risultato pur mantenendo un puntatore non MyStruct in là.

Inoltre, non è necessario utilizzare le strutture inline, è possibile creare quelle con nome. Io uso LiteIDE con il completamento del codice, quindi preferisco in linea per evitare il disordine.

Problemi correlati