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}] = fn
return 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.StructTag
DestTag() reflect.StructTag
Flags() FieldMatchingFlags
Meta() *Meta
}
// 用来给转换函数传递额外的信息,被封装在scope里面
type Meta struct {
KeyNameMapping FieldMappingFunc
Context interface{}
}
type scope struct {
converter *Converter
meta *Meta
flags FieldMatchingFlags
srcStack scopeStack
destStack scopeStack
}
func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {
return s.converter.Convert(src, dest, flags, s.meta)
}
// 返回最新找到的属性的tag
func (s *scope) SrcTag() reflect.StructTag {
return s.srcStack.top().tag
}
// 返回最新找到的属性的tag
func (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.StructTag
value reflect.Value
key string
}
type scopeStack []scopeStackElem
func (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 ConversionFuncs
generatedConversionFuncs ConversionFuncs
// 忽略掉一些类型的转换
ignoredConversions map[typePair]struct{}
ignoredUntypedConversions map[typePair]struct{}
// 实际代码里面两个变量没有作用
// src -> dest
structFieldDests map[typeNamePair][]typeNamePair
// dest -> src
structFieldSources map[typeNamePair][]typeNamePair
// field mapping函数
// 默认注册了map[string][]string{}/url.Values{}
// 对应的field mapping函数为JSONKeyMapper
// 根据结构体tag匹配map的key
inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc
// obj默认匹配flag,包含以下四种
// DestFromSource、SourceToDest、IgnoreMissingFields、AllowDifferentFieldTypeNames
inputDefaultFlags 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.Convert
func (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本身了,不需要处理Ptr
func (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可能被递归调用,因此需要先判断是否有符合条件的转换函数
// 这里需要转换为指针的type
pair = 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 -> struct
case reflect.Struct:
return c.convertKV(toKVValue(sv), toKVValue(dv), scope)
case reflect.Slice:
// 若为切片,则
// 若sv为空,则赋值返回
// 此处支持map/chan/slice/string/array -> slice
if 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 -> map
dv.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 nil
default:
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则遍历src
lister := dkv
if scope.flags.IsSet(SourceToDest) {
lister = skv
}
// 检查是否设置了mapping函数
var mapping FieldMappingFunc
if 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为A
for _, 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,b
stag := skv.tagOf(key)
dtag := dkv.tagOf(key)
// skey为A
// dkey为B
skey := key
dkey := key
if 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的Foo
skey, 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 = skey
scope.srcStack.top().tag = stag
scope.destStack.top().key = dkey
scope.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 := false
if 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.fieldName
scope.destStack.top().key = fieldName
if 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 = fieldName
scope.destStack.top().key = potentialDestKey.fieldName
if 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.Value
if 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() []string
tagOf(key string) reflect.StructTag
value(key string) reflect.Value
confirmSet(key string, v reflect.Value) bool
}
type stringMapAdaptor reflect.Value
func (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.Value
func (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 = nil
return 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
}