mgo 存储的时候是 用json.marshal(in interface{})的方法,
内部调用了
func (e *encoder) addDoc(v reflect.Value) { for { if vi, ok := v.Interface().(Getter); ok { getv, err := vi.GetBSON() if err != nil { panic(err) } v = reflect.ValueOf(getv) continue } if v.Kind() == reflect.Ptr { v = v.Elem() continue } break } if v.Type() == typeRaw { raw := v.Interface().(Raw) if raw.Kind != 0x03 && raw.Kind != 0x00 { panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document") } e.addBytes(raw.Data...) return } start := e.reserveInt32() switch v.Kind() { case reflect.Map: e.addMap(v) case reflect.Struct: e.addStruct(v) case reflect.Array, reflect.Slice: e.addSlice(v) default: panic("Can't marshal " + v.Type().String() + " as a BSON document") } e.addBytes(0) e.setInt32(start, int32(len(e.out)-start)) }
如果是Map的话就调用addMap方法。
func (e *encoder) addMap(v reflect.Value) { for _, k := range v.MapKeys() { e.addElem(k.String(), v.MapIndex(k), false) } }
func (e *encoder) addElemName(kind byte, name string) { e.addBytes(kind) e.addBytes([]byte(name)...) e.addBytes(0) } func (e *encoder) addElem(name string, v reflect.Value, minSize bool) { if !v.IsValid() { e.addElemName('\x0A', name) return } if getter, ok := v.Interface().(Getter); ok { getv, err := getter.GetBSON() if err != nil { panic(err) } e.addElem(name, reflect.ValueOf(getv), minSize) return } switch v.Kind() { case reflect.Interface: e.addElem(name, v.Elem(), minSize) case reflect.Ptr: e.addElem(name, v.Elem(), minSize) case reflect.String: s := v.String() switch v.Type() { case typeObjectId: if len(s) != 12 { panic("ObjectIDs must be exactly 12 bytes long (got " + strconv.Itoa(len(s)) + ")") } e.addElemName('\x07', name) e.addBytes([]byte(s)...) case typeSymbol: e.addElemName('\x0E', name) e.addStr(s) default: e.addElemName('\x02', name) e.addStr(s) } case reflect.Float32, reflect.Float64: e.addElemName('\x01', name) e.addInt64(int64(math.Float64bits(v.Float()))) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: u := v.Uint() if int64(u) < 0 { panic("BSON has no uint64 type, and value is too large to fit correctly in an int64") } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) { e.addElemName('\x10', name) e.addInt32(int32(u)) } else { e.addElemName('\x12', name) e.addInt64(int64(u)) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch v.Type() { case typeMongoTimestamp: e.addElemName('\x11', name) e.addInt64(v.Int()) case typeOrderKey: if v.Int() == int64(MaxKey) { e.addElemName('\x7F', name) } else { e.addElemName('\xFF', name) } default: i := v.Int() if (minSize || v.Type().Kind() != reflect.Int64) && i >= math.MinInt32 && i <= math.MaxInt32 { // It fits into an int32, encode as such. e.addElemName('\x10', name) e.addInt32(int32(i)) } else { e.addElemName('\x12', name) e.addInt64(i) } } case reflect.Bool: e.addElemName('\x08', name) if v.Bool() { e.addBytes(1) } else { e.addBytes(0) } case reflect.Map: e.addElemName('\x03', name) e.addDoc(v) case reflect.Slice: vt := v.Type() et := vt.Elem() if et.Kind() == reflect.Uint8 { e.addElemName('\x05', name) e.addBinary('\x00', v.Bytes()) } else if et == typeDocElem || et == typeRawDocElem { e.addElemName('\x03', name) e.addDoc(v) } else { e.addElemName('\x04', name) e.addDoc(v) } case reflect.Array: et := v.Type().Elem() if et.Kind() == reflect.Uint8 { e.addElemName('\x05', name) e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte)) } else { e.addElemName('\x04', name) e.addDoc(v) } case reflect.Struct: switch s := v.Interface().(type) { case Raw: kind := s.Kind if kind == 0x00 { kind = 0x03 } e.addElemName(kind, name) e.addBytes(s.Data...) case Binary: e.addElemName('\x05', name) e.addBinary(s.Kind, s.Data) case RegEx: e.addElemName('\x0B', name) e.addCStr(s.Pattern) e.addCStr(s.Options) case JavaScript: if s.Scope == nil { e.addElemName('\x0D', name) e.addStr(s.Code) } else { e.addElemName('\x0F', name) start := e.reserveInt32() e.addStr(s.Code) e.addDoc(reflect.ValueOf(s.Scope)) e.setInt32(start, int32(len(e.out)-start)) } case time.Time: // MongoDB handles timestamps as milliseconds. e.addElemName('\x09', name) e.addInt64(s.Unix() * 1000 + int64(s.Nanosecond() / 1e6)) case url.URL: e.addElemName('\x02', name) e.addStr(s.String()) case undefined: e.addElemName('\x06', name) default: e.addElemName('\x03', name) e.addDoc(v) } default: panic("Can't marshal " + v.Type().String() + " in a BSON document") } }
再根据反射的类型存值。
对于 map[int]string
map插入以后变成了 <int value> "asdas",<int value> "adasd"
处理方法是
M is a convenient alias for a map[string]interface{} map, useful for dealing with BSON in a native way.
只能直接用bson.M来存了。