I have a number of structs that require custom marshalling. When I was testing I was using JSON and the standard JSON marshaller. As it doesn't marshal unexported fields, I needed to write a custom MarshalJSON function, which worked perfectly. When I called json.Marshal on the parent struct containing the ones that needed custom marshalling as fields, it worked fine.
Now I need to marshal everything to BSON for some MongoDB work, and I can't find any documentation about how to write custom BSON marshalling. Can anyone tell me how to do the equivalent for BSON/mgo for what I've demonstrated below?
currency.go (the important parts)
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 (again, just the relevant parts)
type Product struct {
Name string
Code string
Price currency.Currency
}
When I call json.Marshal(p) where p is a Product, it produces the output I want without the need for the pattern (not sure of the name) where you create a struct which is just a clone with all exported fields.
In my opinion using the inline method I've used greatly simplifies the API and stops you having extra structs that clutter things up.
Custom bson Marshalling/Unmarshalling works nearly the same way, you have to implement the Getter and Setter interfaces respectively
Something like this should work :
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,
}, nil
}
// 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
}
}