什么是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
// Converter knows how to convert one type to another.
type Converter struct {
// Map from the conversion pair to a function which can
// do the conversion.
//***普通转换函数***//
conversionFuncs ConversionFuncs
//***自动生成的转换函数***//
generatedConversionFuncs ConversionFuncs
// genericConversions are called during normal conversion to offer a "fast-path"
// that avoids all reflection. These methods are not called outside of the .Convert()
// method.
//***优先级最高***//
genericConversions []GenericConversionFunc
// Set of conversions that should be treated as a no-op
//***需要忽略的转换***//
ignoredConversions map[typePair]struct{}
// This is a map from a source field type and name, to a list of destination
// field type and name.
structFieldDests map[typeNamePair][]typeNamePair
// Allows for the opposite lookup of structFieldDests. So that SourceFromDest
// copy flag also works. So this is a map of destination field name, to potential
// source field name and type to look for.
structFieldSources map[typeNamePair][]typeNamePair
// Map from a type to a function which applies defaults.
//***设置默认值函数***//
defaultingFuncs map[reflect.Type]reflect.Value
// Similar to above, but function is stored as interface{}.
defaultingInterfaces map[reflect.Type]interface{}
// Map from an input type to a function which can apply a key name mapping
inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc
// Map from an input type to a set of default conversion flags.
inputDefaultFlags map[reflect.Type]FieldMatchingFlags
// If non-nil, will be called to print helpful debugging info. Quite verbose.
Debug DebugLogger
// nameFunc is called to retrieve the name of a type; this name is used for the
// purpose of deciding whether two types match or not (i.e., will we attempt to
// do a conversion). The default returns the go type name.
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
// NewConverter creates a new Converter object.
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
// scope contains information about an ongoing conversion.
type scope struct {
converter *Converter
meta *Meta
flags FieldMatchingFlags
// srcStack & destStack are separate because they may not have a 1:1
// relationship.
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 (
    // Loop through destination fields, search for matching source
    // field to copy it from. Source fields with no corresponding
    // destination field will be ignored. If SourceToDest is
    // specified, this flag is ignored. If neither is specified,
    // or no flags are passed, this flag is the default.
    DestFromSource FieldMatchingFlags = 0
    // Loop through source fields, search for matching dest field
    // to copy it into. Destination fields with no corresponding
    // source field will be ignored.
    SourceToDest FieldMatchingFlags = 1 << iota
    // Don't treat it as an error if the corresponding source or
    // dest field can't be found.
    IgnoreMissingFields
    // Don't require type names to match.
    AllowDifferentFieldTypeNames
    )
    //***此处的DestFromSource 0作特殊处理,其他的用&操作来判断是否设置***//
    func (f FieldMatchingFlags) IsSet(flag FieldMatchingFlags) bool {
    if flag == DestFromSource {
    // The bit logic doesn't work on the default value.
    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
// Convert continues a conversion.
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
//***注册DefaultingFunc入口***//
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()

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的转换函数优先级为:

  1. 使用genericConversions中的转换函数;
  2. 检查是否在ignoredConversions中,如果是则直接返回;
  3. 使用conversionFuncs中的转换函数;
  4. 使用generatedConversionFuncs中的转换函数;
  5. 使用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 {
// TODO: avoid scope allocation
s := &scope{converter: c, flags: flags, meta: meta}
//***逐个尝试genericConversions中的转换函数***//
for _, fn := range c.genericConversions {
if ok, err := fn(src, dest, s); ok {
return err
}
}
}
//***调用doConversion()***//
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
}
//***dest必须是可写的***//
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,
}
// Leave something on the stack, so that calls to struct tag getters never fail.
s.srcStack.push(scopeStackElem{})
s.destStack.push(scopeStackElem{})
return f(sv, dv, s)
}

convert()

convert()的流程如下:

  1. 获取源和目标的类型;
  2. 设置源的默认值;
  3. 判断是否在ignoredConversions中;
  4. 尝试从conversionFuncs中获取转换函数进行转换;
  5. 尝试从generatedConversionFuncs中获取转换函数进行转换;
  6. 尝试默认的转换函数,即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()
    // Apply default values.
    //***设置默认值***//
    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}
    // ignore conversions of this type
    //***判断是否在ignoredConversions中***//
    if _, ok := c.ignoredConversions[pair]; ok {
    if c.Debug != nil {
    c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt)
    }
    return nil
    }
    // Convert sv to dv.
    //***获取转换函数,并执行***//
    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)
    }
    //***尝试generatedConversionFuncs***//
    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)
    }
    //***Fankang***//
    //***尝试默认的转换函数***//
    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:
// Don't copy these via assignment/conversion!
default:
// This should handle all simple types.
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:
//***如果是struct,则调用convertKV()***//
return c.convertKV(toKVValue(sv), toKVValue(dv), scope)
case reflect.Slice:
if sv.IsNil() {
// Don't make a zero-length slice.
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() {
// Don't copy a nil ptr!
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() {
// Don't copy a nil ptr!
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())
// TODO: sv.MapIndex(sk) may return a value with CanAddr() == false,
// because a map[string]struct{} does not allow a pointer reference.
// Calling a custom conversion function defined for the map value
// will panic. Example is PodInfo map[string]ContainerStatus.
if err := c.convert(sv.MapIndex(sk), dkv, scope); err != nil {
return err
}
dv.SetMapIndex(dk, dkv)
}
case reflect.Interface:
if sv.IsNil() {
// Don't copy a nil interface!
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()的流程如下:

  1. 先根据scope中的flag来确定使用源,还是目标结构体的字段作为标准;
  2. 对字段进行checkField()检查,即使用Converter的structFieldDests和structFieldSources来获取对应关系,然后完成转换,具体见checkField()方法分析。
  3. 接着使用同名字段转换策略,调用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 {
    // TODO: add keys to stack to support really understandable error messages.
    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() {
    //***key: A***//
    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):
    // No error.
    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
    }
    // type Foo struct {
    // A string `test:"foo"`
    // }
    // type Bar struct {
    // A string `test:"bar"`
    // }
    //***skey: A stag: test:"foo" dkey: A dtag: test:"bar"***//
    //***sf.Type(): string df.Type(): string***//
    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}
// 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
}
//***找到df对应的sf***//
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
//***此处调用convert()***//
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
}

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 {
//***此处能获取struct tag***//
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}} //打印出x,类型为A
Convert from *main.A to *main.B //打算从*main.A转换到*main.B
use conversionFuncs main.A main.B //因为注册有func(in *A, out *B, s conversion.Scope) error,所以使用conversionFuncs进行转换
Convert from *int to *int //执行s.Convert(&in.Baz, &out.Baz, 0),准备从*int转换到*int
In defaultConvert() int int //{*int, *int}转换使用defaultConvert()完成转换。
Convert from *main.T1 to *main.T2 //执行s.Convert(&in.A, &out.A, conversion.AllowDifferentFieldTypeNames), 准备*main.T1转换到*main.T2
In defaultConvert() main.T1 main.T2 //在进行{*main.T1, main.T2}转换,未定义转换函数,所以使用defaultConvert(),因为是struct,所以defaultConvert()中使用convertKV()进行转换
In convertKV() string string //转换T1和T2的第一个字段
use conversionFuncs string string //{string, string}的转换函数已经注册,所以使用conversionFuncs进行转换
Get tag test:"foo"
In convertKV() string string //转换T1和T2的第二个字段,{string, string}的转换函数已经注册,所以使用conversionFuncs进行转换
use conversionFuncs string string //{string, string}的转换函数已经注册,所以使用conversionFuncs进行转换
Get tag test1:"foo1"
{I am 3 {years old}} //转换完成,打印y

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定义的来,否则按同名转换原则进行转换;其他的类型也有相应的方法进行转换。