使用反射实现序列化与反序列化

conf.ini
config.ini 
;test
#test
[server]
ip=192.168.0.100
port = 8080

[ mysql]
username=root
password = abc-123
database=test
host=192.168.0.1.1
port=3306

ini_config.go //实现代码
package config

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strconv"
	"strings"
)

//序列化成配置文件
func MarshalFile(data interface{}, filename string) (err error) {
	result, err := Marshal(data)
	if err != nil {
		return
	}
	err = ioutil.WriteFile(filename, result, os.ModePerm)
	return
}

//序列化配置
func Marshal(data interface{}) (result []byte, err error) {
	//获取类型信息
	typeInfo := reflect.TypeOf(data)
	if typeInfo.Kind() != reflect.Struct {
		err = errors.New("please pass struct")
		return
	}

	var confData []string
	valueInfo := reflect.ValueOf(data)
	for i := 0; i < typeInfo.NumField(); i++ {
		sectionField := typeInfo.Field(i)
		sectionValue := valueInfo.Field(i)

		fieldType := sectionField.Type
		if fieldType.Kind() != reflect.Struct {
			continue
		}
		tagValue := sectionField.Tag.Get("ini")
		if len(tagValue) == 0 {
			tagValue = sectionField.Name
		}
		section := fmt.Sprintf("\n[%s]\n", tagValue)
		confData = append(confData, section)
		//fmt.Println(confData)
		for j := 0; j < sectionField.Type.NumField(); j++ {
			keyField := sectionField.Type.Field(j)
			fieldTagVal := keyField.Tag.Get("ini")
			if len(fieldTagVal) == 0 {
				fieldTagVal = keyField.Name
			}
			valueField := sectionValue.Field(j)
			item := fmt.Sprintf("%s=%v\n", fieldTagVal, valueField.Interface())
			confData = append(confData, item)
		}
	}
	for _, value := range confData {
		result = append(result, []byte(value)...)
	}
	return
}

//反序列成结构体
func UnMarshalFile(filename string, result interface{}) (err error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	return UnMarshal(data, result)
}

//反序列化成结构体
func UnMarshal(data []byte, result interface{}) (err error) {

	//获取类型信息
	typeInfo := reflect.TypeOf(result)
	if typeInfo.Kind() != reflect.Ptr {
		err = errors.New("please pass point address")
		return
	}

	//判断指向变量的类型
	typeStruct := typeInfo.Elem()
	if typeStruct.Kind() != reflect.Struct {
		err = errors.New("please pass strut data")
		return
	}

	//遍历配置文件
	lineArr := strings.Split(string(data), "\n")
	var lastFieldName string
	for index, value := range lineArr {
		line := strings.TrimSpace(value)
		if len(line) == 0 { //判断是否为空行,如果是空行就跳过
			continue
		}
		//如果是注释就跳过
		if line[0] == ';' || line[0] == '#' {
			continue
		}
		if line[0] == '[' {
			lastFieldName, err = ParseSection(line, typeStruct, index)
			if err != nil {
				err = fmt.Errorf("%v lineno:%d", err, index+1)
				return
			}
			continue
		}
		//解析节点下的选项
		err = parseItem(lastFieldName, line, result)
		if err != nil {
			err = fmt.Errorf("%v lineno:%d", err, index+1)
			return
		}
	}
	//var m map[string] string
	return
}

func parseItem(lastFieldName string, line string, result interface{}) (err error) {
	//判断节点下的选项
	subIndex := strings.Index(line, "=")
	if subIndex == -1 {
		err = fmt.Errorf("syntax error,line:%s", line)
		return
	}
	key := strings.TrimSpace(line[0:subIndex])
	val := strings.TrimSpace(line[subIndex+1:])

	if len(key) == 0 {
		err = fmt.Errorf("syntax error,line:%s", line)
		return
	}
	resultValue := reflect.ValueOf(result)
	sectionValue := resultValue.Elem().FieldByName(lastFieldName)

	sectionType := sectionValue.Type()
	if sectionType.Kind() != reflect.Struct {
		err = fmt.Errorf("field:%s must be struct", lastFieldName)
		return
	}

	keyFieldName := ""
	for i := 0; i < sectionType.NumField(); i++ {
		field := sectionType.Field(i)
		tagVal := field.Tag.Get("ini")
		if tagVal == key {
			keyFieldName = field.Name
			break
		}
	}

	if len(keyFieldName) == 0 {
		return
	}
	//fmt.Println(keyFieldName,val)
	fieldValue := sectionValue.FieldByName(keyFieldName)
	if fieldValue == reflect.ValueOf(nil) {
		return
	}

	switch fieldKind := fieldValue.Type().Kind(); fieldKind {
	case reflect.String:
		fieldValue.SetString(val)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		intVal, errRet := strconv.ParseInt(val, 10, 64)
		if errRet != nil {
			err = errRet
			return
		}
		fieldValue.SetInt(intVal)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		intVal, errRet := strconv.ParseUint(val, 10, 64)
		if errRet != nil {
			err = errRet
			return
		}
		fieldValue.SetUint(intVal)
	case reflect.Float32, reflect.Float64:
		floatVal, errRet := strconv.ParseFloat(val, 64)
		if errRet != nil {
			err = errRet
			return
		}
		fieldValue.SetFloat(floatVal)
	default:
		err = fmt.Errorf("unsupport type:%v", fieldKind)
		return
	}
	return
}

func ParseSection(line string, typeInfo2 reflect.Type, index int) (fieldName string, err error) {
	//判断节点的合法性
	if line[0] == '[' && len(line) <= 2 {
		err = fmt.Errorf("synatax error,inalid section:%s,line:%d", line, index+1)
		return
	}
	if line[0] == '[' && line[len(line)-1] != ']' {
		err = fmt.Errorf("synatax error,inalid section:%s,line:%d", line, index+1)
		return
	}
	if line[0] == '[' && line[len(line)-1] == ']' {
		sectionName := strings.TrimSpace(line[1 : len(line)-1])
		if len(sectionName) == 0 {
			err = fmt.Errorf("synatax error,inalid section:%s,line:%d", line, index+1)
			return
		}

		for i := 0; i < typeInfo2.NumField(); i++ {
			field := typeInfo2.Field(i)
			tagValue := field.Tag.Get("ini")
			if tagValue == sectionName {
				fieldName = field.Name
				break
			}
		}
	}
	return
}


//测试用例
ini_config_test.go

package config

import (
	"fmt"
	"io/ioutil"
	"testing"
)

type Config struct{
	ServerConf ServerConfig  `ini:"server"`
	MysqlConf MysqlConfig `ini:"mysql"`
}

type ServerConfig struct {
	IP string `ini:"ip"`
	Port int `ini:"port"`
}

type MysqlConfig struct {
   Username string `ini:"username"`
   Password string `ini:"password"`
   Database string `ini:"database"`
   Host string `ini:"host"`
   Port int `ini:"port"`
}

func TestIniConfig(t *testing.T){
	//fmt.Println("hello")
	//t.Error("run failed")
	data,err := ioutil.ReadFile("config.ini")
	if err != nil {
		t.Errorf("read ini config file failed:%v\n",err)
	}
	//fmt.Printf("ini context:%s\n",string(data))

	var conf Config
	err = UnMarshal(data,&conf)
	if err != nil {
		t.Errorf("UnMarshal failed:%v\n",err)
		return
	}
	t.Logf("unmarshal sucess,conf:%#v\n",conf)
	fmt.Printf("\n------------------------------------\n")
	data,err = Marshal(conf)
	if err != nil {
		t.Errorf("marshal faield:%v\n",err)
		return
	}
	t.Logf("marshal sucess:%v\n",string(data))

	fmt.Printf("\n------------------------------------\n")

	/*err = MarshalFile(conf,"./test.ini")
	if err != nil {
		t.Logf("write file failed:%v\n",err)
		return
	}*/

	//UnMarshalFile
	fmt.Printf("\n------------------------------------\n")
	var conf2 Config
	err = UnMarshalFile("test.ini",&conf2)
	if err !=nil {
		t.Errorf("unmarshalFile failed:%v\n",err)
		return
	}
	t.Logf("unmarshalFile sucess:%#v\n",conf2)
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值