• 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
    ", 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
    ", s)
    }
    
    ## 输出
    value: &{Height:0.4 Age:2}
    json:  {"height":0.4,"age":2}
    value: &{Height:1.5 Age:10}
    
  • 相关阅读:
    jQuery找出所有没有disabled属性的checkbox
    jQuery prop()方法
    Aliyun 中PHP如何升级
    The connection to the server localhost:8080 was refused
    ks8集群扩容新增节点,以及xshell无法访问的问题
    设置小程序模板消息keyword_id_list问题
    git如何新建仓库,并初始化代码
    k8s应用配置详解
    git如何把分支变成master
    nginx首页根据IP跳转
  • 原文地址:https://www.cnblogs.com/maomaomaoge/p/14142275.html
Copyright © 2020-2023  润新知