自定义编组到BSON和JSON(使用Golang和mgo)

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

Custom marshalling to bson and JSON (Golang & mgo)

问题

我在Golang中有以下类型:

type Base64Data []byte

为了支持将base64编码的字符串解组到这个类型中,我做了以下操作:

func (b *Base64Data) UnmarshalJSON(data []byte) error {
    if len(data) == 0 {
        return nil
    }

    content, err := base64.StdEncoding.DecodeString(string(data[1 : len(data)-1]))
    if err != nil {
        return err
    }
    
    *b = []byte(content)
    return nil
}

现在我还想能够使用mgo Golang库将其编组和解组到Mongo数据库中。问题是,我已经有存储为base64编码字符串的文档,所以我必须保持这样的格式。我尝试了以下操作:

func (b Base64Data) GetBSON() (interface{}, error) {
    return base64.StdEncoding.EncodeToString([]byte(b)), nil
}

func (b *Base64Data) SetBSON(raw bson.Raw) error {
    var s string
    var err error
    if err = raw.Unmarshal(&s); err != nil {
        return err
    }
    *b, err = base64.StdEncoding.DecodeString(s)
    return err
}

这样,在解组后,数据已经解码,所以我需要将其重新编码,并将其作为字符串返回,以便将其作为字符串写入数据库(反之亦然)。为此,我实现了bson的getter和setter,但似乎只有getter正常工作。

从base64编码的字符串进行JSON解组是有效的,同样将其编组到数据库中也是有效的。但是,似乎根本没有调用解组的setter。

有人能否建议我漏掉了什么,以便我能够正确地将解码后的数据保存在内存中,但作为编码字符串类型返回?

英文:

I have the following type in Golang:

type Base64Data []byte

In order to support unmarshalling a base64 encoded string to this type, I did the following:

func (b *Base64Data) UnmarshalJSON(data []byte) error {
	if len(data) == 0 {
		return nil
	}

	content, err := base64.StdEncoding.DecodeString(string(data[1 : len(data)-1]))
	if err != nil {
		return err
	}
	
	*b = []byte(xml)
	return nil
}

Now I also want to be able to marshal and unmarshal it to mongo database, using mgo Golang library.
The problem is that I already have documents there stored as base64 encoded string, so I have to maintain that.
I tried to do the following:

func (b Base64Data) GetBSON() (interface{}, error) {
	return base64.StdEncoding.EncodeToString([]byte(b)), nil
}

func (b *Base64DecodedXml) SetBSON(raw bson.Raw) error {
	var s string
	var err error
	if err = raw.Unmarshal(&s); err != nil {
		return err
	}
	*b, err = base64.StdEncoding.DecodeString(s)
	return err
}

So that after unmarshaling, the data is already decoded, so I need to encode it back, and return it as a string so it will be written to db as a string (and vice versa)
For that I implemented bson getter and setter, but it seems only the getter is working properly

JSON unmarshaling from base64 encoded string works, as well marshaling it to database. but unmarshling setter seems to not be called at all.

Can anyone suggest what I'm missing, so that I'll be able to properly hold the data decoded in memory, but encoded string type?

This is a test I tried to run:

b := struct {
	Value shared.Base64Data `json:"value" bson:"value"`
}{}
s := `{"value": "PHJvb3Q+aGVsbG88L3Jvb3Q+"}`
require.NoError(t, json.Unmarshal([]byte(s), &b))
t.Logf("%v", string(b.Value))
b4, err := bson.Marshal(b)
require.NoError(t, err)
t.Logf("%v", string(b4))
require.NoError(t, bson.Unmarshal(b4, &b))
t.Logf("%v", string(b.Value))

答案1

得分: 2

你可以使用bson.Marshal()来序列化map和结构体值,但不能序列化其他类型的值。

如果你想进行测试,可以将一个map(例如bson.M)传递给bson.Marshal()

var x = Base64Data{0x01, 0x02, 0x03}

dd, err := bson.Marshal(bson.M{"data": x})
fmt.Println(string(dd), err)

你的代码可以正常工作,并且按照你的意图执行。你可以尝试插入一个包装值来验证:

c := sess.DB("testdb").C("testcoll")

var x = Base64Data{0x01, 0x02, 0x03}
if err := c.Insert(bson.M{
    "data": x,
}); err != nil {
    panic(err)
}

这将把数据保存为Base64编码的字符串形式。

当然,如果你想将其加载回Base64Data类型的值,你还需要定义SetBSON(raw Raw) error方法(bson.Setter接口)。

英文:

You can't marshal any value with bson.Marshal(), only maps and struct values.

If you want to test it, pass a map, e.g. bson.M to bson.Marshal():

var x = Base64Data{0x01, 0x02, 0x03}

dd, err := bson.Marshal(bson.M{"data": x})
fmt.Println(string(dd), err)

Your code works as-is, and as you intend it to. Try to insert a wrapper value to verify it:

c := sess.DB("testdb").C("testcoll")

var x = Base64Data{0x01, 0x02, 0x03}
if err := c.Insert(bson.M{
	"data": x,
}); err != nil {
	panic(err)
}

This will save the data as a string, being the Base64 encoded form.

Of course if you want to load it back into a value of type Base64Data, you will also need to define the SetBSON(raw Raw) error method too (bson.Setter interface).

huangapple
  • 本文由 发表于 2017年2月3日 17:56:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/42021462.html
匿名

发表评论

匿名网友

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

确定