2014-10-10 15 views
5

Possiedo una struttura che vorrei maresciallo in JSON in modo diverso a seconda del contesto.JSON marshalling/unmarshalling stessa struttura in un diverso formato JSON in go?

Ad esempio, a volte voglio schierare in questo modo:

type MyStruct struct { 
     Nickname  string `json:"nickname"` 
     EmailAddress string `json:"email_address"` 
     PhoneNumber string `json:"-"` 
     MailingAddress string `json:"-"` 
    } 

E a volte voglio schierare in questo modo:

type MyStruct struct { 
     Nickname  string `json:"nickname"` 
     EmailAddress string `json:"email_address"` 
     PhoneNumber string `json:"phone_number"` 
     MailingAddress string `json:"mailing_address"` 
    } 

C'è un modo semplice per fare questo senza:

  1. Creazione di 2 strutture separate.
  2. Scrittura di un marshaller personalizzato.
  3. Rimuovere temporaneamente i valori stringa per PhoneNumber e MailingAddress (con un'omitempty sul tag), effettuare il marshalling e quindi aggiungerli nuovamente.

Se solo ci fosse un modo per:

  1. Specificare 2 set di tag e dire al gestore di marshalling quali utilizzare.
  2. Cambia dinamicamente i tag in fase di esecuzione.

risposta

10

So che esplicitamente parla di "senza scrivere un gestore di marshalling personalizzato", ma nel caso in cui qualcuno vede e pensa che dovrebbe essere evitato a causa della complessità, un gestore di marshalling personalizzato per fare ciò che si vuole fare è molto semplice:

type MyStruct struct { 
    Nickname  string `json:"nickname"` 
    EmailAddress string `json:"email_address"` 
    PhoneNumber string `json:"phone_number"` 
    MailingAddress string `json:"mailing_address"` 
    all   bool 
} 

func (ms MyStruct) MarshalJSON() ([]byte, error) { 
    m := map[string]interface{}{} // ideally use make with the right capacity 
    m["nickname"] = ms.Nickname 
    m["email_address"] = ms.EmailAddress 
    if ms.all { 
     m["phone_number"] = ms.PhoneNumber 
     m["mailing_address"] = ms.MailingAddress 
    } 
    return json.Marshal(m) 
} 

Se il campo all deve essere impostato da un pacchetto esterno, quindi un metodo potrebbe essere definita sulla struct, o il campo cou Devo essere reso pubblico (non influenzerebbe il JSON poiché è codificato tramite il marshaler personalizzato).

esempio Runnable sul campo da gioco: http://play.golang.org/p/1N_iBzvuW4

+0

Questo è stato molto più semplice di quanto immaginassi. Ho finito per utilizzare una combinazione di questo approccio e quello descritto qui: http://attilaolah.eu/2014/10/10/json-and-struct-composition-in-go/ –

+0

Ecco alcuni link per riferimento: The Marshaler tipo http://golang.org/pkg/encoding/json/#Marshaler. Articolo del blog: http://blog.golang.org/json-and-go –

+0

Lo stesso principio si applica a unmarshalling: http://golang.org/pkg/encoding/json/#Unmarshaler –

-1

È possibile utilizzare la riflessione, non proprio la soluzione più efficiente, ma è conveniente.

func MarshalSubset(obj interface{}, fields ...string) ([]byte, error) { 
    if len(fields) == 0 { 
     return json.Marshal(obj) 
    } 
    out := make(map[string]interface{}, len(fields)) 
    val := reflect.ValueOf(obj) 
    if val.Kind() == reflect.Ptr { 
     val = val.Elem() 
    } 
    if val.Kind() != reflect.Struct { 
     panic("not a struct") 
    } 
    typ := val.Type() 
    for _, f := range fields { 
     val := val.FieldByName(f).Interface() 
     rfld, _ := typ.FieldByName(f) 
     tag := strings.Split(rfld.Tag.Get("json"), ",") 
     if len(tag) > 0 { 
      f = tag[0] 
     } 
     out[f] = val 
    } 

    return json.Marshal(out) 
} 

playground

Problemi correlati