ShiftStc相同字段的结构体转换

ShiftStc相同字段的结构体转换

巧妙的解决字段转换,尤其是不止20多个字段从grpc格式转化为自己业务model

但是序列化需要时间,用的时候

用的时候只需要自己断言即可

直接上工具代码

func ShiftStc(dst, src interface{}) (interface{}, error) {
	var (
		err error
		marl []byte
	)
	if marl, err = json.Marshal(src); err != nil {
		return nil, err
	}

	if err = json.Unmarshal(marl, dst); err != nil {
		return nil, err
	}

	return dst, nil
}

完整测试代码

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

type CFlor struct {
	NameFlor string
}

type Conf0 struct {
	CFlor *CFlor
	Name0 string
}

type Conf1 struct {
	Name1 string
}

type Conf2 struct {
	Conf0 *Conf0
	Conf1 *Conf1
	Name2 string
}

func newC() *Conf2 {
	return &Conf2{
		Conf0: &Conf0{CFlor:&CFlor{NameFlor:"cf"},Name0: "c0"},
		Conf1: &Conf1{Name1:"c1"},
		Name2: "c2",
	}
}

type AFlor struct {
	NameFlor string
}

type Aonf0 struct {
	CFlor AFlor
	Name0 string
}

type Aonf1 struct {
	Name1 string
}

type Aonf2 struct {
	Conf0 Aonf0
	Conf1 Aonf1
	Name2 string
}


func newA() *Aonf2 {
	return &Aonf2{
		Conf0: Aonf0{CFlor: AFlor{NameFlor:"af"},Name0:"a0"},
		Conf1: Aonf1{Name1:"a1"},
		Name2: "a2",
	}
}

func ShiftStc(dst, src interface{}) (interface{}, error) {
	var (
		err error
		marl []byte
	)
	if marl, err = json.Marshal(src); err != nil {
		return nil, err
	}

	if err = json.Unmarshal(marl, dst); err != nil {
		return nil, err
	}

	return dst, nil
}

func main() {
	a := newA()
	c := newC()
	stc, _ := ShiftStc(a, c)
	aonf2, ok  := stc.(*Aonf2)
	if !ok {
		return
	}

	fmt.Println(aonf2.Conf0)
}

## 代码输出
{{cf} c0}

基本的反射

package main

import (
	"fmt"
	"reflect"
)

type CFlor struct {
	NameFlor string
}

type Conf0 struct {
	CFlor *CFlor
	Name0 string
}

type Conf1 struct {
	Name1 string
}

type Conf2 struct {
	Conf0 *Conf0
	Conf1 *Conf1
	Name2 string
}

func newC() *Conf2 {
	return &Conf2{
		Conf0: &Conf0{CFlor:&CFlor{NameFlor:"cf"},Name0: "c0"},
		Conf1: &Conf1{Name1:"c1"},
		Name2: "c2",
	}
}

type AFlor struct {
	NameFlor string
}

type Aonf0 struct {
	CFlor AFlor
	Name0 string
}

type Aonf1 struct {
	Name1 string
}

type Aonf2 struct {
	Conf0 Aonf0
	Conf1 Aonf1
	Name2 string
}


func newA() *Aonf2 {
	return &Aonf2{
		Conf0: Aonf0{CFlor: AFlor{NameFlor:"af"},Name0:"a0"},
		Conf1: Aonf1{Name1:"a1"},
		Name2: "a2",
	}
}

func base1() {
    fmt.Println("base1:NumField基础模块探究--------------------------")
	a := newA()
	valA := reflect.ValueOf(a).Elem()
	field := valA.NumField()
	fmt.Println(field)

	c := newC()
	valC := reflect.ValueOf(c).Elem()
	field2 := valC.NumField()
	fmt.Println(field2)
	fmt.Println("带指针的结构体不能用NumField,需要Elem()一下")
}

func base2() {
	fmt.Println("base2测试结构体位置带指针的包含几个--------------------------")
	a := newA()
	valA := reflect.ValueOf(a).Elem()
	fmt.Println(valA.NumField())

	c := newC()
	valC := reflect.ValueOf(c).Elem()
	fmt.Println(valC.NumField())
	fmt.Println("都是三个,可以总结,反射并不能识别嵌套里面的字段")
}

func main() {
	base1()
	base2()
}

## 输出
base1:NumField基础模块探究--------------------------
3
3
带指针的结构体不能用NumField,需要Elem()一下
base2测试结构体位置带指针的包含几个--------------------------
3
3
都是三个,可以总结,反射并不能识别嵌套里面的字段

反射泥潭

我们想获取结构体的字段,需要用Typeof(),但是对于grpc来说,这个东西都是嵌套的结构体带指针。我们嵌套时候。直接把A扔进B就好,只在头部取地址,数据一样是地址传递,丢不了。详情看我上一篇文章: [StructTransmit值传递和引用传递](https://www.cnblogs.com/maomaomaoge/p/14128499.html)

第一反应

通过获取结构体标签去遍历

痛点

  1. grpc的json都是大写。也就是双方有的人没按照规范。

  2. 对于我所实验的结构体反射,还需要判断是不是地址,再去取出来字段。下面我们来参照go-ini包核心源码,主要转换函数是MapTo(&c),直接点进去,一步步看就好

    // mapTo maps a section to object v.
    func (s *Section) mapTo(v interface{}, isStrict bool) error {
    	typ := reflect.TypeOf(v)
    	val := reflect.ValueOf(v)
    	if typ.Kind() == reflect.Ptr {
    		typ = typ.Elem()
    		val = val.Elem()
    	} else {
    		return errors.New("not a pointer to a struct")
    	}
    
    	if typ.Kind() == reflect.Slice {
    		newField, err := s.mapToSlice(s.name, val, isStrict)
    		if err != nil {
    			return err
    		}
    
    		val.Set(newField)
    		return nil
    	}
    
    	return s.mapToField(val, isStrict, 0, s.name)
    }
    

    在继续看mapToField函数源码

    // mapToField maps the given value to the matching field of the given section.
    // The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
    func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error {
    	if val.Kind() == reflect.Ptr {
    		val = val.Elem()
    	}
    	typ := val.Type()
    
    	for i := 0; i < typ.NumField(); i++ {
    		field := val.Field(i)
    		tpField := typ.Field(i)
    
    		tag := tpField.Tag.Get("ini")
    		if tag == "-" {
    			continue
    		}
    
    		rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
    		fieldName := s.parseFieldName(tpField.Name, rawName)
    		if len(fieldName) == 0 || !field.CanSet() {
    			continue
    		}
    
    		isStruct := tpField.Type.Kind() == reflect.Struct
    		isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
    		isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
    		if isAnonymousPtr {
    			field.Set(reflect.New(tpField.Type.Elem()))
    		}
    
    		if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) {
    			if isStructPtr && field.IsNil() {
    				field.Set(reflect.New(tpField.Type.Elem()))
    			}
    			fieldSection := s
    			if rawName != "" {
    				sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName
    				if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) {
    					fieldSection = secs[sectionIndex]
    				}
    			}
    			if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil {
    				return fmt.Errorf("map to field %q: %v", fieldName, err)
    			}
    		} else if isAnonymousPtr || isStruct || isStructPtr {
    			if secs, err := s.f.SectionsByName(fieldName); err == nil {
    				if len(secs) <= sectionIndex {
    					return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName)
    				}
    				// Only set the field to non-nil struct value if we have a section for it.
    				// Otherwise, we end up with a non-nil struct ptr even though there is no data.
    				if isStructPtr && field.IsNil() {
    					field.Set(reflect.New(tpField.Type.Elem()))
    				}
    				if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil {
    					return fmt.Errorf("map to field %q: %v", fieldName, err)
    				}
    				continue
    			}
    		}
    
    		// Map non-unique sections
    		if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
    			newField, err := s.mapToSlice(fieldName, field, isStrict)
    			if err != nil {
    				return fmt.Errorf("map to slice %q: %v", fieldName, err)
    			}
    
    			field.Set(newField)
    			continue
    		}
    
    		if key, err := s.GetKey(fieldName); err == nil {
    			delim := parseDelim(tpField.Tag.Get("delim"))
    			if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
    				return fmt.Errorf("set field %q: %v", fieldName, err)
    			}
    		}
    	}
    	return nil
    }
    

    可以看到,大多是还是通过标签判断的,但是各种的typeof判断类型,已经是极其复杂了,所以,还是舍弃这个方案。

看官方Example包代码

这个官方代码可以说是,很不全。想要的都没有,甚至各种的io操作,让你很蒙蔽,但是看看会发现,他们的高逼格买还是有点启发的

官方的恶心之处

看各种博客可以看到反射的Typeof()是多么简洁易懂,看下面这个恶心的官方,与其说是标准,我愿意称之为装逼

package main

import (
	"fmt"
	"io"
	"os"
	"reflect"
)

func main() {
	// As interface types are only used for static typing, a
	// common idiom to find the reflection Type for an interface
	// type Foo is to use a *Foo value.
	writerType := reflect.TypeOf((*io.Writer)(nil)).Elem()

	fileType := reflect.TypeOf((*os.File)(nil))
	fmt.Println(fileType.Implements(writerType))

}

奇妙之处

高逼格的代码渗透出知识全面,需要自己去越过这门槛,下面这代码就隐含主题

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
)

func main() {
	typ := reflect.StructOf([]reflect.StructField{
		{
			Name: "Height",
			Type: reflect.TypeOf(float64(0)),
			Tag:  `json:"height"`,
		},
		{
			Name: "Age",
			Type: reflect.TypeOf(int(0)),
			Tag:  `json:"age"`,
		},
	})

	v := reflect.New(typ).Elem()
	v.Field(0).SetFloat(0.4)
	v.Field(1).SetInt(2)
	s := v.Addr().Interface()

	w := new(bytes.Buffer)
	if err := json.NewEncoder(w).Encode(s); err != nil {
		panic(err)
	}

	fmt.Printf("value: %+v\n", s)
	fmt.Printf("json:  %s", w.Bytes())

	r := bytes.NewReader([]byte(`{"height":1.5,"age":10}`))
	if err := json.NewDecoder(r).Decode(s); err != nil {
		panic(err)
	}
	fmt.Printf("value: %+v\n", s)
}

## 输出
value: &{Height:0.4 Age:2}
json:  {"height":0.4,"age":2}
value: &{Height:1.5 Age:10}
posted @ 2020-12-16 09:41  maob  阅读(96)  评论(0编辑  收藏  举报