处理自定义的BSON编组

huangapple go评论85阅读模式
英文:

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编组/解组几乎以相同的方式进行,您需要分别实现GetterSetter接口。

类似这样的代码应该可以工作:

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
	}
}

huangapple
  • 本文由 发表于 2015年6月17日 20:14:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/30891301.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定