2016-10-19 5 views
0

Go에서 정수와 문자열 간의 매핑을 위해 사용자 정의 JSON marshaller/unmarshaller를 사용하고 있습니다. 문제는 값이 문자열 대신 정수로 데이터베이스에 저장된다는 것입니다. 아래의 예에서, 나는 이것이 MongoDB를 데이터베이스에 저장 될 기대 : MongoDB는 값을 저장하기 전에 마샬링하지 않습니다.

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

대신 내가 얻을 : 테스트에서 볼 수 있듯이

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

를, 정렬 화 및 비 정렬 화는 잘 작동하고 있습니다. 무슨 일이야?

다음은 Go 테스트 (unmarshal_test.go에 저장하고 "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()) 
    } 
} 

편집 : 추가 더 많은 테스트는 마샬링과 언 마샬링가 작동 증명합니다.

답변

1

bson 인코더는 JSON 마샬링 인터페이스를 사용하지 않습니다. Getter 인터페이스를 구현 : 또한 Setter 인터페이스를 구현하는 것이 좋습니다

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

.

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

Setter 인터페이스에도 메모를 추가하는 데 문제가 없으면 허용 된 답변으로 표시해 드리겠습니다. 감사! –

관련 문제