golang 反射_golang利用反射来实现一个简单的csv配置读取模块

922e8a741ee5fac7180ef5038dc457a2.png

csv或者其他配置读取功能在服务器开发中属于基础模块,而在开发过程中,csv等配置文件免不了越来越多,而这些代码的定义以及csv读取稍显繁琐以及重复工作。因此想到用反射来实现一个表格的读取,基本一个csv表格,只需要增添几行代码,且十分容易查阅。

type csvDataConfig struct {
	StringInterfaceConf struct {
		TestInterfaceString String `key:"" val:"test"`
	} `table:"string_interface_data"`

	StringConf struct {
		TestString string `key:"test" val:""`
	} `table:"string_data"`
}

每次新增表格或者字段,只需要修改或者增添上面的struct,table定义csv表名。key表示要取的字段名称,即csv的横列,val则代表csv的纵列。核心思想是把一张表定义成一个struct ,然后表名和row、column都用tag来表示,方便阅读。

上述定义的csv图表如下图

8ee2caef8dc23b91bac24784a0a73b9c.png
string_data.csv

0733e0150f298e321cbf5ea2cb568ac1.png
string_interface_data.csv

调用方式如下:

log.Println("config.StringConf.TestString:", config.StringConf.TestString)

testInterface, err := config.StringInterfaceConf.TestInterfaceString(2)
if err != nil {
    log.Panic("config.StringInterfaceConf.TestInterfaceString(2) failed")
}

log.Println("config.StringConf.TestInterfaceString(2):", testInterface)

总体应该说比较简洁,且方便浏览代码

下面来讲解下代码实现:

核心代码如下:

//将 String 定义成 func(i ...interface{}) (string, error) 
type (
	String func(i ...interface{}) (string, error)
)
//利用反射将结构体赋值,分表格设置成对应的结构体
func LoadCsvDataConf() (errs []error) {
	CsvDataConfLock.Lock()
	defer CsvDataConfLock.Unlock()

	csvDataConf = csvDataConfig{}
	va := reflect.ValueOf(&csvDataConf).Elem()
	vt := reflect.TypeOf(csvDataConf)
	for i := 0; i < va.NumField(); i++ {
		vf := va.Field(i)
		vtf := vt.Field(i)
		for j := 0; j < vf.NumField(); j++ {
			cd := newCsvData()
			err := cd.getCsvData(vtf, vf.Type().Field(j))
			if err != nil {
				errs = append(errs, err)
				continue
			}
			err = cd.setCsvDataConf(vf.Field(j))
			if err != nil {
				errs = append(errs, fmt.Errorf("table: %s name: %s error: %v", cd.Table, vf.Field(j).Type().Name(), err))
			}
		}
	}
	return
}
//After getCsvData,then can set csvData config correctly.
func (c *CsvData) setCsvDataConf(rv reflect.Value) error {
	if !rv.CanSet() {
		return fmt.Errorf("%v can not set", rv)
	}
	switch rv.Kind() {
	case reflect.String:
		r, err := c.getString()
		if err != nil {
			return err
		}
		rv.SetString(r)
	case reflect.Func:
		switch c.Type {
		case "String":
			rv.Set(reflect.ValueOf(c.getString))
		}

		return nil
	}

	return nil
}
//根据各种Tag设置每个csvdata的表名,row,和column
func (c *CsvData) getCsvData(tableTag, base reflect.StructField) error {
	err := c.getBase(tableTag.Tag, base.Tag)
	if err != nil {
		return err
	}
	c.Type = base.Type.Name()
	return nil
}

func (c *CsvData) getBase(tableTag, rs reflect.StructTag) error {

	table := tableTag.Get("table")
	if table == "" {
		return fmt.Errorf("Tag:%v table not set", rs)
	}

	key := rs.Get("key")
	val := rs.Get("val")
	if key == "" && val == "" {
		//return fmt.Errorf("Tag:%v key and val not set", rs)
	}

	if val == "" {
		val = "val"
	}

	c.Table = table
	c.Key = key
	c.Val = val
	return nil
}

func (c *CsvData) getString(k ...interface{}) (string, error) {
	s, err := c.getStringBase(k)
	if err != nil {
		return s, err
	}
	return s, nil
}
//获取对应的字段值,GetString()函数即是取值函数,可自行补充
func (c *CsvData) getStringBase(k []interface{}) (string, error) {
	key := c.Key
	if len(k) > 1 {
		return "", fmt.Errorf("In parameters number:%v more than limited number:1", len(k))
	} else if len(k) == 1 {
		key = fmt.Sprint(k[0])
	}
	s, ret := GetString(c.Table, key, c.Val)
	if ret != GAMEDATA_OK {
		return s, fmt.Errorf("GetInt( %s, %s) failed:%d", c.Key, c.Val, ret)
	}
	return s, nil
}

上面定义了两种格式,即普通的string类型,和自己定义的String类型,分别用于表格的key:val 模式,和row:column模式,至于其他的,都可以由这两种转换而来,就不再赘述。

以上。

首次写文,不知所云,见谅

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值