2015-06-17 12 views
8

Ho un numero di strutture che richiedono il marshalling personalizzato. Durante i test stavo usando JSON e il normale marshaller JSON. Dato che non esegue il marshalling di campi non esportati, avevo bisogno di scrivere una funzione MarshalJSON personalizzata, che funzionava perfettamente. Quando ho chiamato json.Marshal sulla struttura padre contenente quelli che avevano bisogno del marshalling personalizzato come campi, ha funzionato correttamente.Gestione personalizzata BSON Marshaling (Golang & mgo)

Ora ho bisogno di eseguire il marshalling di tutto su BSON per alcuni lavori di MongoDB e non riesco a trovare alcuna documentazione su come scrivere il marshalling BSON personalizzato. Qualcuno può dirmi come fare l'equivalente per BSON/mgo per quello che ho dimostrato di seguito?

currency.go (le parti più importanti)

type Currency struct { 
    value  decimal.Decimal //The actual value of the currency. 
    currencyCode string   //The ISO currency code. 
} 

/* 
MarshalJSON implements json.Marshaller. 
*/ 
func (c Currency) MarshalJSON() ([]byte, error) { 
    f, _ := c.Value().Float64() 
    return json.Marshal(struct { 
     Value  float64 `json:"value" bson:"value"` 
     CurrencyCode string `json:"currencyCode" bson:"currencyCode"` 
    }{ 
     Value:  f, 
     CurrencyCode: c.CurrencyCode(), 
    }) 
} 

/* 
UnmarshalJSON implements json.Unmarshaller. 
*/ 
func (c *Currency) UnmarshalJSON(b []byte) error { 

    decoded := new(struct { 
     Value  float64 `json:"value" bson:"value"` 
     CurrencyCode string `json:"currencyCode" bson:"currencyCode"` 
    }) 

    jsonErr := json.Unmarshal(b, decoded) 

    if jsonErr == nil { 
     c.value = decimal.NewFromFloat(decoded.Value) 
     c.currencyCode = decoded.CurrencyCode 
     return nil 
    } else { 
     return jsonErr 
    } 
} 

product.go (ancora una volta, solo le parti rilevanti)

type Product struct { 
    Name string 
    Code string 
    Price currency.Currency 
} 

Quando chiamo json.Marshal (p) dove p è un prodotto, produce l'output desiderato senza la necessità del modello (non è sicuro del nome) in cui si crea una struttura che è solo un clone con tutti i campi esportati.

A mio parere, l'utilizzo del metodo inline che ho utilizzato semplifica notevolmente l'API e impedisce di disporre di strutture aggiuntive che ingombrano le cose.

+0

Suppongo che il fatto che la struct valuta è dichiarato utilizzando campi non esportati è un refuso? – SirDarius

+1

No, è intenzionale. C'è più codice di ciò che è sopra e io sono un grande sostenitore dell'uso di getter/setter per impedire al programmatore di cambiare semplicemente ciò che vogliono senza riguardo per la logica di business immutabile, più lo strato di astrazione significa qualsiasi cambiamento più avanti sulla linea al funzionamento interno della mia struttura significa che devo cambiare una quantità minima di codice. – leylandski

+0

Inoltre, se si è un utente del pacchetto 'shopspring/decimal', i campi non vengono esportati, quindi in entrambi i casi sarà comunque necessario definire un Getter/Setter personalizzato per abilitare la serializzazione/deserializzazione come mostrato nel risposta. –

risposta

13

personalizzato BSON Marshalling/deserializzazione funziona quasi allo stesso modo, è necessario implementare le interfacce Getter e Setter rispettivamente

Qualcosa del genere dovrebbe funzionare:

type Currency struct { 
    value  decimal.Decimal //The actual value of the currency. 
    currencyCode string   //The ISO currency code. 
} 

// GetBSON implements bson.Getter. 
func (c Currency) GetBSON() (interface{}, error) { 
    f := c.Value().Float64() 
    return struct { 
     Value  float64 `json:"value" bson:"value"` 
     CurrencyCode string `json:"currencyCode" bson:"currencyCode"` 
    }{ 
     Value:  f, 
     CurrencyCode: c.currencyCode, 
    } 
} 

// SetBSON implements bson.Setter. 
func (c *Currency) SetBSON(raw bson.Raw) error { 

    decoded := new(struct { 
     Value  float64 `json:"value" bson:"value"` 
     CurrencyCode string `json:"currencyCode" bson:"currencyCode"` 
    }) 

    bsonErr := raw.Unmarshal(decoded) 

    if bsonErr == nil { 
     c.value = decimal.NewFromFloat(decoded.Value) 
     c.currencyCode = decoded.CurrencyCode 
     return nil 
    } else { 
     return bsonErr 
    } 
} 
+1

Questo è esattamente quello che stavo cercando! È piuttosto fastidioso che non l'abbiano chiamato in modo simile a quello JSON, ma cosa si può fare. – leylandski

+2

L'unica modifica necessaria per rendere questa una soluzione perfetta è rimuovere l'istruzione bson.Marshal attorno alla struttura di ritorno nel metodo get come descritto da [questa risposta] (http://stackoverflow.com/questions/30895854/why-wont -mgo-unmarshall-my-struct-properly/30897080 # 30897080) – leylandski

+0

@Adam: Infatti, grazie. Modificato la mia risposta per rispecchiarlo – HectorJ