什么是Converter
Converter可以完成不同结构体之间的转换。在Kubernetes中,Converter用来把一个版本的object转换成其他版本的object,转换函数需要通过注册的方式添加到Converter中。
先来看下Converter的定义,Converter定义在/pkg/conversion/converter.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| type Converter struct { conversionFuncs ConversionFuncs generatedConversionFuncs ConversionFuncs genericConversions []GenericConversionFunc ignoredConversions map[typePair]struct{} structFieldDests map[typeNamePair][]typeNamePair structFieldSources map[typeNamePair][]typeNamePair defaultingFuncs map[reflect.Type]reflect.Value defaultingInterfaces map[reflect.Type]interface{} inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc inputDefaultFlags map[reflect.Type]FieldMatchingFlags Debug DebugLogger nameFunc func(t reflect.Type) string }
|
Converter结构体的字段涵义如下:
- conversionFuncs: 普通转换函数,通过RegisterConversionFunc()方法进行注册;
- generatedConversionFuncs: 自动生成的转换函数,通过RegisterGeneratedConversionFunc()方法进行注册;
- genericConversions:通用转换函数,优化级最高的转换函数,可以理解为转换的快速通道,通过AddGenericConversionFunc()方法进行注册;
- ignoredConversions:需要忽略的转换,如果两个结构体之间不允许转换,则通过RegisterIgnoredConversion()方法进行注册;
- structFieldDests:源结构体中字段到目标结构体中的字段的映射关系,通过SetStructFieldCopy()方法进行注册;
- structFieldSources:与structFieldDests相反,是目的结构体中字段到源结构体中的字段的映射关系,通过SetStructFieldCopy()方法进行注册;
- defaultingFuncs:设置结构体值的默认值,通过RegisterDefaultingFunc()方法进行注册;
- inputFieldMappingFuncs:暂不知道用途;
- inputDefaultFlags:暂不知道用途;
- nameFunc:获取结构体名称的函数。
Converter的生成方法如下,只要传入NameFunc即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| func NewConverter(nameFn NameFunc) *Converter { c := &Converter{ conversionFuncs: NewConversionFuncs(), generatedConversionFuncs: NewConversionFuncs(), ignoredConversions: make(map[typePair]struct{}), defaultingFuncs: make(map[reflect.Type]reflect.Value), defaultingInterfaces: make(map[reflect.Type]interface{}), 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.RegisterConversionFunc(Convert_Slice_byte_To_Slice_byte) return c }
|
关于NameFunc,只要实现返回一个结构体的“名字”就行,如:
1
| var DefaultNameFunc = func(t reflect.Type) string { return t.Name() }
|
在介绍Converter的方法之前,先介绍ConversionFuncs和scope两个概念。
ConversionFuncs
在Converter的字段中,不管是conversionFuncs,还是generatedConversionFuncs,都是结构体ConversionFuncs。结构体ConversionFuncs用来管理转换函数,定义在/pkg/conversin/converter.go中:
1 2 3 4 5 6 7 8
| type typePair struct { source reflect.Type dest reflect.Type } type ConversionFuncs struct { fns map[typePair]reflect.Value }
|
在ConversionFuncs结构体中,只有一个字段fns map[typePair]reflect.Value
,即{source, dest}到转换函数的映射表。
再来看ConversionFuncs支持的方法。
Add()
ConversionFuncs的Add()方法的参数是转换函数(func(type1, type2, Scope) error
),然后提取转换函数第一个参数的类型作为source,提取第二个参数的类型作为dest,然后把{source, dest}作为key,转换函数作为value,一起存储到fns字段中。
1 2 3 4 5 6 7 8 9 10 11
| func (c ConversionFuncs) Add(fns ...interface{}) error { for _, fn := range fns { fv := reflect.ValueOf(fn) ft := fv.Type() if err := verifyConversionFunctionSignature(ft); err != nil { return err } c.fns[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv } return nil }
|
Merge()
ConversionFuncs的Merge()方法可以把两个ConversionFuncs中的转换函数融合成一个ConversionFuncs。
1 2 3 4 5 6 7 8 9 10
| func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs { merged := NewConversionFuncs() for k, v := range c.fns { merged.fns[k] = v } for k, v := range other.fns { merged.fns[k] = v } return merged }
|
Scope
Scope是一个interface,所以我们来分析其实现者scope。
scope包含了递归转换中需要的一些内容。先来看下scope的定义,同样在/pkg/conversion/converter.go中:
1 2 3 4 5 6 7 8 9 10 11
| type scope struct { converter *Converter meta *Meta flags FieldMatchingFlags srcStack scopeStack destStack scopeStack }
|
来看scope的字段:
- converter:scope包含一个converter,以方便递归转换;
- meta:暂不知道用途;
flags:转换时字段的策略,定义如下,可以使用IsSet()方法判断对应的flag是否被设置。
其中:
DestFromSource表示循环目标结构体字段,寻找合适的源结构体字段;
SourceToDest表示循环源结构体字段,寻找合适的目标结构体字段;
IgnoreMissingFields表示如果未找到合适的目标不报错;
AllowDifferentFieldTypeNames表示允许源结构体和目标结构体不同的类型相匹配。
具体定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| type FieldMatchingFlags int const ( DestFromSource FieldMatchingFlags = 0 SourceToDest FieldMatchingFlags = 1 << iota IgnoreMissingFields AllowDifferentFieldTypeNames ) func (f FieldMatchingFlags) IsSet(flag FieldMatchingFlags) bool { if flag == DestFromSource { return f&SourceToDest != SourceToDest } return f&flag == flag }
|
srcStack, destStack:字段信息栈。scopeStack定义如下,其中scopeStackElem表示字段,包含了key, value, tag三部分信息;scopeStack实现了scopeStackElem栈,以配合递归转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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] }
|
可以看出,scope包含了converter。之前说过,转换函数一般为func(type1, type2, Scope) error
,如果转换函数中有转换的需要,则可以使用scope的converter进行进一步转换。
Convert()
scope封装了converter.Convert()方法。
1 2 3 4
| func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error { return s.converter.Convert(src, dest, flags, s.meta) }
|
注册方法
Converter注册类有方法是供注册时调用的方法。主要有:
RegisterConversionFunc()
RegisterConversionFunc()可以把conversionFunc注册到Converter的conversionFuncs中。
1 2 3
| func (c *Converter) RegisterConversionFunc(conversionFunc interface{}) error { return c.conversionFuncs.Add(conversionFunc) }
|
RegisterGeneratedConversionFunc()
RegisterGeneratedConversionFunc()可以把conversionFunc注册到Converter的generatedConversionFuncs中。
1 2 3 4
| func (c *Converter) RegisterGeneratedConversionFunc(conversionFunc interface{}) error { return c.generatedConversionFuncs.Add(conversionFunc) }
|
RegisterIgnoredConversion()
RegisterIgnoredConversion()可以把需要忽略的{from, to}注册到Converter的ignoredConversions中。
1 2 3 4 5 6 7 8 9 10 11 12
| func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error { typeFrom := reflect.TypeOf(from) typeTo := reflect.TypeOf(to) if reflect.TypeOf(from).Kind() != reflect.Ptr { return fmt.Errorf("expected pointer arg for 'from' param 0, got: %v", typeFrom) } if typeTo.Kind() != reflect.Ptr { return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo) } c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{} return nil }
|
RegisterDefaultingFunc()
RegisterDefaultingFunc()可以把defaultingFunc注册到Converter的defaultingFuncs和defaultingInterfaces中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func (c *Converter) RegisterDefaultingFunc(defaultingFunc interface{}) error { fv := reflect.ValueOf(defaultingFunc) ft := fv.Type() if ft.Kind() != reflect.Func { return fmt.Errorf("expected func, got: %v", ft) } if ft.NumIn() != 1 { return fmt.Errorf("expected one 'in' param, got: %v", ft) } if ft.NumOut() != 0 { return fmt.Errorf("expected zero 'out' params, got: %v", ft) } if ft.In(0).Kind() != reflect.Ptr { return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft) } inType := ft.In(0).Elem() c.defaultingFuncs[inType] = fv c.defaultingInterfaces[inType] = defaultingFunc return nil }
|
RegisterInputDefaults()可以把相关内容注册到Converter的inputFieldMappingFuncs和inputDefaultFlags中。但inputFieldMappingFuncs和inputDefaultFlags的具体用途目前还不清楚。
1 2 3 4 5 6 7 8 9 10
| func (c *Converter) RegisterInputDefaults(in interface{}, fn FieldMappingFunc, defaultFlags FieldMatchingFlags) error { fv := reflect.ValueOf(in) ft := fv.Type() if ft.Kind() != reflect.Ptr { return fmt.Errorf("expected pointer 'in' argument, got: %v", ft) } c.inputFieldMappingFuncs[ft] = fn c.inputDefaultFlags[ft] = defaultFlags return nil }
|
SetStructFieldCopy()
SetStructFieldCopy()把结构体间字段关系注册到Converter的structFieldDests和structFieldSources中。结构体间字段关系转换优先级高于同名字段转换。
1 2 3 4 5 6 7 8 9
| func (c *Converter) SetStructFieldCopy(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error { st := reflect.TypeOf(srcFieldType) dt := reflect.TypeOf(destFieldType) srcKey := typeNamePair{st, srcFieldName} destKey := typeNamePair{dt, destFieldName} c.structFieldDests[srcKey] = append(c.structFieldDests[srcKey], destKey) c.structFieldSources[destKey] = append(c.structFieldSources[destKey], srcKey) return nil }
|
转换方法
Converter的转换函数优先级为:
- 使用genericConversions中的转换函数;
- 检查是否在ignoredConversions中,如果是则直接返回;
- 使用conversionFuncs中的转换函数;
- 使用generatedConversionFuncs中的转换函数;
- 使用defaultConvert()方法转换,defaultConvert()方法会递归式地对字段进行转换。
Convert()
Convert()是Converter的转换的入口。Convert()先尝试使用genericcConversions中的转换函数去完成转换,如果成功,则返回;否则,调用doConversion(),传入的conversionFunc为convert()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error { if len(c.genericConversions) > 0 { s := &scope{converter: c, flags: flags, meta: meta} for _, fn := range c.genericConversions { if ok, err := fn(src, dest, s); ok { return err } } } return c.doConversion(src, dest, flags, meta, c.convert) }
|
DefaultConvert()
DefaultConvert()直接调用doConversion()方法进行转换。传入的conversionFunc为defaultConvert()方法。
1 2 3
| func (c *Converter) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error { return c.doConversion(src, dest, flags, meta, c.defaultConvert) }
|
doConversion()
doConversion()在进行一系列的检查后,调用参数f,即convert()或defaultConvert()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error { dv, err := EnforcePtr(dest) if err != nil { return err } if !dv.CanAddr() && !dv.CanSet() { return fmt.Errorf("can't write to dest") } sv, err := EnforcePtr(src) if err != nil { return err } s := &scope{ converter: c, flags: flags, meta: meta, } s.srcStack.push(scopeStackElem{}) s.destStack.push(scopeStackElem{}) return f(sv, dv, s) }
|
convert()
convert()的流程如下:
- 获取源和目标的类型;
- 设置源的默认值;
- 判断是否在ignoredConversions中;
- 尝试从conversionFuncs中获取转换函数进行转换;
- 尝试从generatedConversionFuncs中获取转换函数进行转换;
- 尝试默认的转换函数,即defaultConvert()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error { dt, st := dv.Type(), sv.Type() if fv, ok := c.defaultingFuncs[st]; ok { if c.Debug != nil { c.Debug.Logf("Applying defaults for '%v'", st) } args := []reflect.Value{sv.Addr()} fv.Call(args) } pair := typePair{st, dt} if _, ok := c.ignoredConversions[pair]; ok { if c.Debug != nil { c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt) } return nil } if fv, ok := c.conversionFuncs.fns[pair]; ok { if c.Debug != nil { c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt) } return c.callCustom(sv, dv, fv, scope) } if fv, ok := c.generatedConversionFuncs.fns[pair]; ok { if c.Debug != nil { c.Debug.Logf("Calling generated conversion of '%v' to '%v'", st, dt) } return c.callCustom(sv, dv, fv, scope) } return c.defaultConvert(sv, dv, scope) }
|
defaultConvert()
defaultConvert()在转换时,如果源字段是基础类型,则调用reflect.Value的Set()方法直接对目标字段设置;否则按照目标字段的类型(此处按目标字段类型是可以先对源类型进行处理,然后再转换)进行不同处理,这里可能使用的就是递归转换。
因为defaultConvert()中定义有对基础类型的转换函数,所以递归迟早会结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| func (c *Converter) defaultConvert(sv, dv reflect.Value, scope *scope) error { dt, st := dv.Type(), sv.Type() if !dv.CanSet() { return scope.errorf("Cannot set dest. (Tried to deep copy something with unexported fields?)") } 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) } switch st.Kind() { case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct: default: if st.AssignableTo(dt) { dv.Set(sv) return nil } if st.ConvertibleTo(dt) { dv.Set(sv.Convert(dt)) return nil } } if c.Debug != nil { c.Debug.Logf("Trying to convert '%v' to '%v'", st, dt) } scope.srcStack.push(scopeStackElem{value: sv}) scope.destStack.push(scopeStackElem{value: dv}) defer scope.srcStack.pop() defer scope.destStack.pop() switch dv.Kind() { case reflect.Struct: return c.convertKV(toKVValue(sv), toKVValue(dv), scope) case reflect.Slice: if sv.IsNil() { dv.Set(reflect.Zero(dt)) return nil } 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 } 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 }
|
convertKV()
convertKV()可以完成struct之间的转换。当然,struct之间的转换函数是未被定义的,不然流程不会走到defaultConvert()。
convertKV()的流程如下:
- 先根据scope中的flag来确定使用源,还是目标结构体的字段作为标准;
- 对字段进行checkField()检查,即使用Converter的structFieldDests和structFieldSources来获取对应关系,然后完成转换,具体见checkField()方法分析。
- 接着使用同名字段转换策略,调用convert()转换字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| 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) } lister := dkv if scope.flags.IsSet(SourceToDest) { lister = skv } var mapping FieldMappingFunc if scope.meta != nil && scope.meta.KeyNameMapping != nil { mapping = scope.meta.KeyNameMapping } for _, key := range lister.keys() { if found, err := c.checkField(key, skv, dkv, scope); found { if err != nil { return err } continue } stag := skv.tagOf(key) dtag := dkv.tagOf(key) skey := key dkey := key if mapping != nil { skey, dkey = scope.meta.KeyNameMapping(key, stag, dtag) } 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 if err := c.convert(sf, df, scope); err != nil { return err } } return nil }
|
checkField()
checkField()如果设置了DestFromSource flag,则使用先根据目标字段找到源字段的策略找到源字段,然后调用convert()进行转换。如果未设置DestFromSource flag,则使用根据源字段找到目标字段的策略进行转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| 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} for _, potentialSourceKey := range c.structFieldSources[destKey] { sf := skv.value(potentialSourceKey.fieldName) if !sf.IsValid() { continue } if sf.Type() == potentialSourceKey.fieldType { 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} for _, potentialDestKey := range c.structFieldDests[srcKey] { df := dkv.value(potentialDestKey.fieldName) if !df.IsValid() { continue } if df.Type() == potentialDestKey.fieldType { 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 }
|
Demo
最后介绍两个Demo来说明Converter的用法。这两个Demo都取自源码的test文件。调用之前,请确保kubernetes及其依赖都在go的指定目录下。为了更清楚地描述转换过程,在源代码里添加了些打印信息。
Demo1
Demo1完成了两个复杂类之间的转换,还包含了struct tag的使用(字段信息存储在scope的srcStack和destStack中)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| package main import ( "fmt" "reflect" "k8s.io/kubernetes/pkg/conversion" ) var DefaultNameFunc = func(t reflect.Type) string { return t.Name() } func main() { type T1 struct { A string `test:"foo"` B string `test1:"foo1"` } type T2 struct { A string `test:"bar"` B string `test1:"bar1"` } type A struct { Foo string Baz int A T1 } type B struct { Bar string Baz int A T2 } c := conversion.NewConverter(DefaultNameFunc) err := c.RegisterConversionFunc( func(in *A, out *B, s conversion.Scope) error { out.Bar = in.Foo s.Convert(&in.Baz, &out.Baz, 0) s.Convert(&in.A, &out.A, conversion.AllowDifferentFieldTypeNames) return nil }) if err != nil { fmt.Printf("unexpected error %v", err) } err = c.RegisterConversionFunc( func(in *string, out *string, s conversion.Scope) error { fmt.Println("Get tag", s.SrcTag()) *out = *in return nil }, ) if err != nil { fmt.Printf("Unexpected error: %v", err) } x := A{"I am ", 3, T1{"years", "old"}} y := B{} fmt.Println(x) err = c.Convert(&x, &y, 0, nil) fmt.Println(y) }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| {I am 3 {years old}} Convert from *main.A to *main.B use conversionFuncs main.A main.B Convert from *int to *int In defaultConvert() int int Convert from *main.T1 to *main.T2 In defaultConvert() main.T1 main.T2 In convertKV() string string use conversionFuncs string string Get tag test:"foo" In convertKV() string string use conversionFuncs string string Get tag test1:"foo1" {I am 3 {years old}}
|
Demo2
Demo2说明了预定义字段映射关系的用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package main import ( "fmt" "reflect" "k8s.io/kubernetes/pkg/conversion" ) var DefaultNameFunc = func(t reflect.Type) string { return t.Name() } func main() { type WeirdMeta struct { Name string Type string } type NameMeta struct { Name string } type TypeMeta struct { Type string } type A struct { WeirdMeta } type B struct { TypeMeta NameMeta } c := conversion.NewConverter(DefaultNameFunc) err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta") if err != nil { fmt.Printf("unexpected error %v", err) } err = c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", NameMeta{}, "NameMeta") if err != nil { fmt.Printf("unexpected error %v", err) } err = c.SetStructFieldCopy(TypeMeta{}, "TypeMeta", WeirdMeta{}, "WeirdMeta") if err != nil { fmt.Printf("unexpected error %v", err) } err = c.SetStructFieldCopy(NameMeta{}, "NameMeta", WeirdMeta{}, "WeirdMeta") if err != nil { fmt.Printf("unexpected error %v", err) } aVal := &A{ WeirdMeta: WeirdMeta{ Name: "Foo", Type: "Bar", }, } bVal := &B{ TypeMeta: TypeMeta{"Bar"}, NameMeta: NameMeta{"Foo"}, } fmt.Println(aVal) err = c.Convert(aVal, bVal, conversion.AllowDifferentFieldTypeNames, nil) if err != nil { fmt.Println(err) } fmt.Println(bVal) }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12
| &{{Foo Bar}} //打印aVal Convert from *main.A to *main.B //准备从*main.A转换到*main.B In defaultConvert() main.A main.B //因为没有注册{*main.A, *main.B}的转换函数,所以默认使用defaultConvert() In checkField() convert from main.WeirdMeta to main.TypeMeta //因为注册有字段对应关系,所以使用checkField(),默认使用是的从目标字段查找源字段。准备从*main.WeirdMeta转换到*main.TypeMeta。 In defaultConvert() main.WeirdMeta main.TypeMeta //因为没有注册{*main.WeirdMeta, *main.TypeMeta}的转换函数,所以使用defaultConvert() In convertKV() string string //因为是结构体,所以将调用convertKV(),找到同名的字段,开始转换 In defaultConvert() string string //因为没有注册{*string, *string}的转换函数,所以使用defaultConvert()完成转换 In checkField() convert from main.WeirdMeta to main.NameMeta //目标结构体的第二个字段,准备从*main.WeirdMeta转换到*main.NameMeta In defaultConvert() main.WeirdMeta main.NameMeta //因为没有注册{*main.WeirdMeta, *main.NameMeta}的转换函数,所以使用defaultConvert() In convertKV() string string //因为是结构体,所以将调用convertKV(),找到同名的字段,开始转换 In defaultConvert() string string //因为没有注册{*string, *string}的转换函数,所以使用defaultConvert()完成转换 &{{Bar} {Foo}} //转换完成,打印bVal
|
总结
Converter先使用genericConversionFunc进行转换,如果不成功,再使用conversionFunc进行转换,如果不成功,再按generatedConversionFunc进行转换,如果不成功,最后交由DefaultConvert()转换。
DefaultConvert()中如果转换类型是简单类型,则直接转换;如果是结构体,则先看是否有字段关系定义,如果有,则按map定义的来,否则按同名转换原则进行转换;其他的类型也有相应的方法进行转换。