dongtaidai0492 2016-10-19 18:09
浏览 22
已采纳

MongoDB在存储值之前未将其编组

I'm using a custom JSON marshaller/unmarshaller for a mapping between integers and strings in Go. The problem is that values are being stored in the database as integers instead of strings. In the example below, I would expect this to be stored in the MongoDB database:

{ "_id" : "id123", "desc" : "Red Delicious", "value" : "apple" }

Instead I get:

{ "_id" : "id123", "desc" : "Red Delicious", "value" : 1 }

As the test shows, marshalling and unmarshalling are working fine. What's going on?

Here's an example as a Go test (save to unmarshal_test.go and "go test").

package testunmarshal

import (
    "fmt"
    "testing"
    "encoding/json"
    mgo "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)

type Const int

const (
    Apple Const = 1
    Banana = 2
    Cherry = 4
)

type Record struct {
    Id string `bson:"_id" json:"id"`
    Desc string `bson:"desc" json:"desc"`
    Value Const `bson:"value" json:"value`
}

func (intValue Const) Code() string {
    switch intValue {
    case Apple: return "apple"
    case Banana: return "banana"
    case Cherry: return "cherry"
    }
    return "invalid"
}

func (intValue *Const) UnmarshalJSON(data []byte) (err error) {
    switch string(data) {
    case `"apple"`: 
        *intValue = Apple
    case `"banana"`:
        *intValue = Banana
    case `"cherry"`:
        *intValue = Cherry
    default:
        return fmt.Errorf("Invalid fruit %s", data)
    }
    return nil
}

func (intValue *Const) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf(`"%s"`, intValue.Code())), nil
}

func TestMarshalJSON(t *testing.T) {
    var orig = Record {
        Id: "id456",
        Desc: "Cavendish",
        Value: Banana,
    }

    var copy Record

    bytes, err := json.Marshal(&orig)

    if err != nil {
        t.Errorf("Marshal failed: %s", err.Error())
        return
    }

    err = json.Unmarshal(bytes, &copy)

    if err != nil {
        t.Errorf("Unmarshal failed: %s", err.Error())
        return
    }

    if orig.Value != copy.Value {
        t.Errorf("Expected %d=%s, got %d=%s", orig.Value, orig.Value.Code(), copy.Value, copy.Value.Code())
    }
}

func TestMarshalBSON(t *testing.T) {
    var orig = Record {
        Id: "id456",
        Desc: "Cavendish",
        Value: Banana,
    }

    var copy Record

    bytes, err := bson.Marshal(&orig)

    if err != nil {
        t.Errorf("Marshal failed: %s", err.Error())
        return
    }

    err = bson.Unmarshal(bytes, &copy)

    if err != nil {
        t.Errorf("Unmarshal failed: %s", err.Error())
        return
    }

    if orig.Value != copy.Value {
        t.Errorf("Expected %d=%s, got %d=%s", orig.Value, orig.Value.Code(), copy.Value, copy.Value.Code())
    }
}

func TestMongo(t *testing.T) {
    var rec1 = Record {
        Id: "id123",
        Desc: "Red Delicious",
        Value: Apple,
    }
    var rec2 Record

    sess, err := mgo.Dial("localhost")

    if err != nil {
        t.Errorf(err.Error())
        return
    }

    db := sess.DB("test")

    if db == nil {
        t.Fatal("Failed to connect to database")
        return
    }

    col := db.C("fruit")

    if col == nil {
        t.Fatal("Failed to open collection")
        return
    }

    // defer col.DropCollection()

    err = col.Insert(&rec1)

    if err != nil {
        t.Fatal("Failed to insert: %s", err.Error())
        return
    }

    err = col.Find(bson.M{}).One(&rec2)

    if err != nil {
        t.Fatal("Failed to retrieve stored object: %s", err.Error())
        return
    }

    if rec1.Value != rec2.Value {
        t.Errorf("Expected %d=%s, got %d=%s", rec1.Value, rec1.Value.Code(), rec1.Value, rec2.Value.Code())
    }
}

Edit: Added more tests to demonstrate that marshalling and unmarshalling are working.

  • 写回答

1条回答 默认 最新

  • dongqieli4164 2016-10-19 19:06
    关注

    The bson encoder does not use the JSON marshaling interfaces. Implement the Getter interface:

    func (intValue Const) GetBSON() (interface{}, error) {
        return intValue.Code(), nil
    }
    

    You will also want to implement the Setter interface.

    func (intValue *Const) SetBSON(raw bson.Raw) error {
        var data int
        if err := raw.Unmarshal(&data); err != nil {
            return err
        }
        switch data {
        case `"apple"`: 
            *intValue = Apple
        case `"banana"`:
            *intValue = Banana
        case `"cherry"`:
            *intValue = Cherry
        default:
            return fmt.Errorf("Invalid fruit %s", data)
        }
        return nil
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 MATLAB代码补全插值
  • ¥15 Typegoose 中如何使用 arrayFilters 筛选并更新深度嵌套的子文档数组信息
  • ¥15 前后端分离的学习疑问?
  • ¥15 stata实证代码答疑
  • ¥50 husky+jaco2实现在gazebo与rviz中联合仿真
  • ¥15 dpabi预处理报错:Error using y_ExtractROISignal (line 251)
  • ¥15 在虚拟机中配置flume,无法将slave1节点的文件采集到master节点中
  • ¥15 husky+kinova jaco2 仿真
  • ¥15 zigbee终端设备入网失败
  • ¥15 金融监管系统怎么对7+4机构进行监管的