英文:
Handling Custom BSON Marshaling
问题
我有一些需要自定义编组的结构体。在测试时,我使用了 JSON 和标准的 JSON 编组器。由于它不会编组未公开的字段,所以我需要编写一个自定义的 MarshalJSON 函数,这个函数运行得很好。当我对包含需要自定义编组的字段的父结构体调用 json.Marshal 时,它也能正常工作。
现在,我需要将所有内容编组为 BSON,用于一些 MongoDB 的工作,但我找不到关于如何编写自定义 BSON 编组的文档。有人可以告诉我如何为 BSON/mgo 编写与下面所示相同的代码吗?
currency.go(重要部分)
type Currency struct {
value decimal.Decimal // 货币的实际值。
currencyCode string // ISO 货币代码。
}
/*
MarshalJSON 实现了 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 实现了 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(同样,只包含相关部分)
type Product struct {
Name string
Code string
Price currency.Currency
}
当我调用 json.Marshal(p),其中 p 是一个 Product,它会产生我想要的输出,而无需使用克隆所有公开字段的结构体的模式(不确定名称)。
在我看来,使用我所使用的内联方法极大地简化了 API,并且避免了使事情变得混乱的额外结构体。
英文:
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.
答案1
得分: 23
自定义的bson编组/解组几乎以相同的方式进行,您需要分别实现Getter和Setter接口。
类似这样的代码应该可以工作:
type Currency struct {
value decimal.Decimal // 货币的实际值。
currencyCode string // ISO货币代码。
}
// GetBSON 实现了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 实现了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
}
}
英文:
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
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论