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)
第一反应
通过获取结构体标签去遍历
痛点
-
grpc的json都是大写。也就是双方有的人没按照规范。
-
对于我所实验的结构体反射,还需要判断是不是地址,再去取出来字段。下面我们来参照
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}