![922e8a741ee5fac7180ef5038dc457a2.png](https://i-blog.csdnimg.cn/blog_migrate/f5e1e78eb86c7c4c8b5bb1ddda619913.jpeg)
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](https://i-blog.csdnimg.cn/blog_migrate/091b58c44f247d5a20dccc9147c18242.png)
![0733e0150f298e321cbf5ea2cb568ac1.png](https://i-blog.csdnimg.cn/blog_migrate/4ec53450dc50f3c93fdf6a977edcec47.png)
调用方式如下:
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模式,至于其他的,都可以由这两种转换而来,就不再赘述。
以上。
首次写文,不知所云,见谅