conversion负责转换两个obj,挨个遍历其中一个obj的属性,找到key或者tag一样的另一边的属性并进行赋值。若有用户注册的转换函数,则调用用户注册的转换函数。
1、ConversionFuncs
type ConversionFuncs struct {untyped map[typePair]ConversionFunc}// 注册一个转换函数// typePair里面的src和dest为指针func (c ConversionFuncs) AddUntyped(a, b interface{}, fn ConversionFunc) error {tA, tB := reflect.TypeOf(a), reflect.TypeOf(b)if tA.Kind() != reflect.Ptr {return fmt.Errorf("the type %T must be a pointer to register as an untyped conversion", a)}if tB.Kind() != reflect.Ptr {return fmt.Errorf("the type %T must be a pointer to register as an untyped conversion", b)}c.untyped[typePair{tA, tB}] = fnreturn nil}// 合并func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs {merged := NewConversionFuncs()for k, v := range c.untyped {merged.untyped[k] = v}for k, v := range other.untyped {merged.untyped[k] = v}return merged}
2、Scope
type Scope interface {Convert(src, dest interface{}, flags FieldMatchingFlags) error// SrcTag和DestTag返回的是当前正在比较的两个属性// 供转换函数进行更进一步的转换SrcTag() reflect.StructTagDestTag() reflect.StructTagFlags() FieldMatchingFlagsMeta() *Meta}// 用来给转换函数传递额外的信息,被封装在scope里面type Meta struct {KeyNameMapping FieldMappingFuncContext interface{}}type scope struct {converter *Convertermeta *Metaflags FieldMatchingFlagssrcStack scopeStackdestStack scopeStack}func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {return s.converter.Convert(src, dest, flags, s.meta)}// 返回最新找到的属性的tagfunc (s *scope) SrcTag() reflect.StructTag {return s.srcStack.top().tag}// 返回最新找到的属性的tagfunc (s *scope) DestTag() reflect.StructTag {return s.destStack.top().tag}func (s *scope) Flags() FieldMatchingFlags {return s.flags}// Meta returns the meta object that was originally passed to Convert.func (s *scope) Meta() *Meta {return s.meta}func (s *scope) setIndices(src, dest int) {s.srcStack.top().key = fmt.Sprintf("[%v]", src)s.destStack.top().key = fmt.Sprintf("[%v]", dest)}func (s *scope) setKeys(src, dest interface{}) {s.srcStack.top().key = fmt.Sprintf(`["%v"]`, src)s.destStack.top().key = fmt.Sprintf(`["%v"]`, dest)}func (s *scope) describe() (src, dest string) {return s.srcStack.describe(), s.destStack.describe()}func (s *scope) errorf(message string, args ...interface{}) error {srcPath, destPath := s.describe()where := fmt.Sprintf("converting %v to %v: ", srcPath, destPath)return fmt.Errorf(where+message, args...)}type scopeStackElem struct {tag reflect.StructTagvalue reflect.Valuekey string}type scopeStack []scopeStackElemfunc (s *scopeStack) pop() {n := len(*s)*s = (*s)[:n-1]}func (s *scopeStack) push(e scopeStackElem) {*s = append(*s, e)}func (s *scopeStack) top() *scopeStackElem {return &(*s)[len(*s)-1]}func (s scopeStack) describe() string {desc := ""if len(s) > 1 {desc = "(" + s[1].value.Type().String() + ")"}for i, v := range s {if i < 2 {// First layer on stack is not real; second is handled specially above.continue}if v.key == "" {desc += fmt.Sprintf(".%v", v.value.Type())} else {desc += fmt.Sprintf(".%v", v.key)}}return desc}
3、Converter
type Converter struct {// 注册的转换函数// generatedConversionFuncs一般为脚手架生成的转换函数// 用户自定义注入到conversionFuncs// 且conversionFuncs的优先级比generatedConversionFuncs高conversionFuncs ConversionFuncsgeneratedConversionFuncs ConversionFuncs// 忽略掉一些类型的转换ignoredConversions map[typePair]struct{}ignoredUntypedConversions map[typePair]struct{}// 实际代码里面两个变量没有作用// src -> deststructFieldDests map[typeNamePair][]typeNamePair// dest -> srcstructFieldSources map[typeNamePair][]typeNamePair// field mapping函数// 默认注册了map[string][]string{}/url.Values{}// 对应的field mapping函数为JSONKeyMapper// 根据结构体tag匹配map的keyinputFieldMappingFuncs map[reflect.Type]FieldMappingFunc// obj默认匹配flag,包含以下四种// DestFromSource、SourceToDest、IgnoreMissingFields、AllowDifferentFieldTypeNamesinputDefaultFlags map[reflect.Type]FieldMatchingFlags// 日志Debug DebugLogger// 类型名称函数nameFunc func(t reflect.Type) string}// 初始化函数并注册切片转换函数func NewConverter(nameFn NameFunc) *Converter {c := &Converter{conversionFuncs: NewConversionFuncs(),generatedConversionFuncs: NewConversionFuncs(),ignoredConversions: make(map[typePair]struct{}),ignoredUntypedConversions: make(map[typePair]struct{}),nameFunc: nameFn,structFieldDests: make(map[typeNamePair][]typeNamePair),structFieldSources: make(map[typeNamePair][]typeNamePair),inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc),inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags),}c.RegisterUntypedConversionFunc((*[]byte)(nil), (*[]byte)(nil),func(a, b interface{}, s Scope) error {return Convert_Slice_byte_To_Slice_byte(a.(*[]byte), b.(*[]byte), s)},)return c}// src/dest必须为指针// meta是用来给转换函数传递信息的,被用来封装在scope中// Scheme调用Scheme.Convert之前会调用DefaultMeta获取flags和meta并传入Converter.Convertfunc (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {return c.doConversion(src, dest, flags, meta, c.convert)}// 开启转换// 首先检查是否注册了转换函数// 否则用默认的转换函数进行转换func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error {pair := typePair{reflect.TypeOf(src), reflect.TypeOf(dest)}scope := &scope{converter: c,flags: flags,meta: meta,}// 是否被忽略if _, ok := c.ignoredUntypedConversions[pair]; ok {return nil}// 优先检查用户自定义转换函数if fn, ok := c.conversionFuncs.untyped[pair]; ok {return fn(src, dest, scope)}// 检查是否有用脚手架生成的转换函数if fn, ok := c.generatedConversionFuncs.untyped[pair]; ok {return fn(src, dest, scope)}// 确保obj为ptr且不为nil并返回obj的Value对象dv, err := EnforcePtr(dest)if err != nil {return err}// 对于dest对象,必须可以取址和设置(要给dest赋值,必须满足这两个条件,src则不需要)if !dv.CanAddr() && !dv.CanSet() {return fmt.Errorf("can't write to dest")}sv, err := EnforcePtr(src)if err != nil {return err}// 保留空的scopeStackElem,包装struct tag getters不会失败// Leave something on the stack, so that calls to struct tag getters never fail.scope.srcStack.push(scopeStackElem{})scope.destStack.push(scopeStackElem{})return f(sv, dv, scope)}// 未匹配到注册函数,则进入默认转换函数// 此处的value已经是struct本身了,不需要处理Ptrfunc (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {dt, st := dv.Type(), sv.Type()pair := typePair{st, dt}// 是否被忽略if _, ok := c.ignoredConversions[pair]; ok {return nil}// 开始转换// 由于convert可能被递归调用,因此需要先判断是否有符合条件的转换函数// 这里需要转换为指针的typepair = typePair{reflect.PtrTo(sv.Type()), reflect.PtrTo(dv.Type())}if f, ok := c.conversionFuncs.untyped[pair]; ok {return c.callUntyped(sv, dv, f, scope)}if f, ok := c.generatedConversionFuncs.untyped[pair]; ok {return c.callUntyped(sv, dv, f, scope)}// 检查dv是否可以被设置if !dv.CanSet() {return scope.errorf("Cannot set dest. (Tried to deep copy something with unexported fields?)")}// 若未设置AllowDifferentFieldTypeNames,但是field名称不一样,则直接抛异常if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.nameFunc(dt) != c.nameFunc(st) {return scope.errorf("type names don't match (%v, %v), and no conversion 'func (%v, %v) error' registered.",c.nameFunc(st), c.nameFunc(dt), st, dt)}// 检查src对象是否为简单类型switch st.Kind() {case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:default:// 若为简单类型直接,则// 若二者类型一样,直接赋值返回// 否则检查是否可以st -> dt,可以直接赋值返回if st.AssignableTo(dt) {dv.Set(sv)return nil}if st.ConvertibleTo(dt) {dv.Set(sv.Convert(dt))return nil}}scope.srcStack.push(scopeStackElem{value: sv})scope.destStack.push(scopeStackElem{value: dv})defer scope.srcStack.pop()defer scope.destStack.pop()// 检查目标对象的类型switch dv.Kind() {// 若为结构体类型,直接调用convertKV进行比较// 此处支持struct -> struct, map -> structcase reflect.Struct:return c.convertKV(toKVValue(sv), toKVValue(dv), scope)case reflect.Slice:// 若为切片,则// 若sv为空,则赋值返回// 此处支持map/chan/slice/string/array -> sliceif sv.IsNil() {dv.Set(reflect.Zero(dt))return nil}// 否则对dv进行挨个赋值dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))for i := 0; i < sv.Len(); i++ {scope.setIndices(i, i)if err := c.convert(sv.Index(i), dv.Index(i), scope); err != nil {return err}}case reflect.Ptr:if sv.IsNil() {dv.Set(reflect.Zero(dt))return nil}dv.Set(reflect.New(dt.Elem()))switch st.Kind() {case reflect.Ptr, reflect.Interface:return c.convert(sv.Elem(), dv.Elem(), scope)default:return c.convert(sv, dv.Elem(), scope)}case reflect.Map:if sv.IsNil() {dv.Set(reflect.Zero(dt))return nil}// 此处支持map -> mapdv.Set(reflect.MakeMap(dt))for _, sk := range sv.MapKeys() {dk := reflect.New(dt.Key()).Elem()if err := c.convert(sk, dk, scope); err != nil {return err}dkv := reflect.New(dt.Elem()).Elem()scope.setKeys(sk.Interface(), dk.Interface())if err := c.convert(sv.MapIndex(sk), dkv, scope); err != nil {return err}dv.SetMapIndex(dk, dkv)}case reflect.Interface:if sv.IsNil() {dv.Set(reflect.Zero(dt))return nil}tmpdv := reflect.New(sv.Elem().Type()).Elem()if err := c.convert(sv.Elem(), tmpdv, scope); err != nil {return err}dv.Set(reflect.ValueOf(tmpdv.Interface()))return nildefault:return scope.errorf("couldn't copy '%v' into '%v'; didn't understand types", st, dt)}return nil}func (c *Converter) convertKV(skv, dkv kvValue, scope *scope) error {if skv == nil || dkv == nil {return fmt.Errorf("unable to convert %#v to %#v", skv, dkv)}// 默认遍历dest,若设置了SourceToDest则遍历srclister := dkvif scope.flags.IsSet(SourceToDest) {lister = skv}// 检查是否设置了mapping函数var mapping FieldMappingFuncif scope.meta != nil && scope.meta.KeyNameMapping != nil {mapping = scope.meta.KeyNameMapping}// eg.// type TestA struct {// A string `test:"a"`// B string `test:"b"`// }// type TestB struct {// A string `test:"b"`// B string `test:"a"`// }// 假设此处遍历TestA// key为Afor _, key := range lister.keys() {// found均为false,忽略即可if found, err := c.checkField(key, skv, dkv, scope); found {if err != nil {return err}continue}// 获取key对应的tag// stag/dtag分别为a,bstag := skv.tagOf(key)dtag := dkv.tagOf(key)// skey为A// dkey为Bskey := keydkey := keyif mapping != nil {// 若配置了mapping函数,一般是针对map -> struct,map -> map,struct -> struct的映射// x := map[string][]string{// "Foo": {"bar"},// "test": {"baz"},// }// type A struct {// Foo string `json:"test"`// }// func JSONKeyMapper(key string, sourceTag, destTag reflect.StructTag) (string, string) {// if s := destTag.Get("json"); len(s) > 0 {// return strings.SplitN(s, ",", 2)[0], key// }// if s := sourceTag.Get("json"); len(s) > 0 {// return key, strings.SplitN(s, ",", 2)[0]// }// return key, key// }// 1、遍历dest。则key为Foo。stag/dtag分别为""/test。因此mapping之后// skey/dkey分别为test/Foo,后续用x的test的值去赋值A的Foo// 2、遍历src。// 第一次遍历:key为Foo。stag/dtag分别为""/test。因此mapping之后// skey/dkey分别为test/Foo,后续用x的test的值去赋值A的Foo// 第二次遍历:key为test。stag/dtag分别为""/""。因此mapping之后// skey/dkey分别为test/test,后续用x的test的值去赋值A的Fooskey, dkey = scope.meta.KeyNameMapping(key, stag, dtag)}// df/sf分别为typeOf(A)/typeOf(A)df := dkv.value(dkey)sf := skv.value(skey)if !df.IsValid() || !sf.IsValid() {switch {case scope.flags.IsSet(IgnoreMissingFields):case scope.flags.IsSet(SourceToDest):return scope.errorf("%v not present in dest", dkey)default:return scope.errorf("%v not present in src", skey)}continue}scope.srcStack.top().key = skeyscope.srcStack.top().tag = stagscope.destStack.top().key = dkeyscope.destStack.top().tag = dtag// 转换后为TestB A的值等于TestB A的值if err := c.convert(sf, df, scope); err != nil {return err}}return nil}// 基本没有用// 因为structFieldSources都是空的func (c *Converter) checkField(fieldName string, skv, dkv kvValue, scope *scope) (bool, error) {replacementMade := falseif scope.flags.IsSet(DestFromSource) {df := dkv.value(fieldName)if !df.IsValid() {return false, nil}destKey := typeNamePair{df.Type(), fieldName}// Check each of the potential source (type, name) pairs to see if they're// present in sv.for _, potentialSourceKey := range c.structFieldSources[destKey] {sf := skv.value(potentialSourceKey.fieldName)if !sf.IsValid() {continue}if sf.Type() == potentialSourceKey.fieldType {// Both the source's name and type matched, so copy.scope.srcStack.top().key = potentialSourceKey.fieldNamescope.destStack.top().key = fieldNameif err := c.convert(sf, df, scope); err != nil {return true, err}dkv.confirmSet(fieldName, df)replacementMade = true}}return replacementMade, nil}sf := skv.value(fieldName)if !sf.IsValid() {return false, nil}srcKey := typeNamePair{sf.Type(), fieldName}// Check each of the potential dest (type, name) pairs to see if they're// present in dv.for _, potentialDestKey := range c.structFieldDests[srcKey] {df := dkv.value(potentialDestKey.fieldName)if !df.IsValid() {continue}if df.Type() == potentialDestKey.fieldType {// Both the dest's name and type matched, so copy.scope.srcStack.top().key = fieldNamescope.destStack.top().key = potentialDestKey.fieldNameif err := c.convert(sf, df, scope); err != nil {return true, err}dkv.confirmSet(potentialDestKey.fieldName, df)replacementMade = true}}return replacementMade, nil}func (c *Converter) callUntyped(sv, dv reflect.Value, f ConversionFunc, scope *scope) error {if !dv.CanAddr() {return scope.errorf("cant addr dest")}var svPointer reflect.Valueif sv.CanAddr() {svPointer = sv.Addr()} else {svPointer = reflect.New(sv.Type())svPointer.Elem().Set(sv)}dvPointer := dv.Addr()return f(svPointer.Interface(), dvPointer.Interface(), scope)}
4、工具
4.1、adaptor
type kvValue interface {keys() []stringtagOf(key string) reflect.StructTagvalue(key string) reflect.ValueconfirmSet(key string, v reflect.Value) bool}type stringMapAdaptor reflect.Valuefunc (a stringMapAdaptor) len() int {return reflect.Value(a).Len()}func (a stringMapAdaptor) keys() []string {v := reflect.Value(a)keys := make([]string, v.Len())for i, v := range v.MapKeys() {if v.IsNil() {continue}switch t := v.Interface().(type) {case string:keys[i] = t}}return keys}func (a stringMapAdaptor) tagOf(key string) reflect.StructTag {return ""}func (a stringMapAdaptor) value(key string) reflect.Value {return reflect.Value(a).MapIndex(reflect.ValueOf(key))}func (a stringMapAdaptor) confirmSet(key string, v reflect.Value) bool {return true}type structAdaptor reflect.Valuefunc (a structAdaptor) len() int {v := reflect.Value(a)return v.Type().NumField()}func (a structAdaptor) keys() []string {v := reflect.Value(a)t := v.Type()keys := make([]string, t.NumField())for i := range keys {keys[i] = t.Field(i).Name}return keys}func (a structAdaptor) tagOf(key string) reflect.StructTag {v := reflect.Value(a)field, ok := v.Type().FieldByName(key)if ok {return field.Tag}return ""}func (a structAdaptor) value(key string) reflect.Value {v := reflect.Value(a)return v.FieldByName(key)}func (a structAdaptor) confirmSet(key string, v reflect.Value) bool {return true}
4.2、通用函数
// 切片转换函数// out可以不用初始化,声明即可func Convert_Slice_byte_To_Slice_byte(in *[]byte, out *[]byte, s Scope) error {if *in == nil {*out = nilreturn nil}*out = make([]byte, len(*in))copy(*out, *in)return nil}// 确保obj为ptr且不为nil并返回obj的Value对象func EnforcePtr(obj interface{}) (reflect.Value, error) {v := reflect.ValueOf(obj)if v.Kind() != reflect.Ptr {if v.Kind() == reflect.Invalid {return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind")}return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type())}if v.IsNil() {return reflect.Value{}, fmt.Errorf("expected pointer, but got nil")}return v.Elem(), nil}
