1, the background

The model layer does not allow json tags to be declared, the DTO layer repeats the wheel, and a table may have 20 fields, so the assignment statement is very uncomfortable.

The second problem is that json directly parses the model layer’s time. time.

Such as

{
    "user_name": "xiaoli"."create_time": "The 2020-06-05 T13:53:06. 293614 + 08:00"
}
Copy the code

In this case, if it cannot be resolved, a DTO must be rewritten.

So how to solve this problem? I thought about it for a period of time, and finally used Map to solve it.

2. Solve problems

1, the reflection

Some people pass in Pointers to everything, some people hide various interface{} conversions, but it’s just too different.

So we need to solve, how to get the Value object exactly, and here is a utility class THAT I wrote

func GetRealValue(value reflect.Value) reflect.Value {
	kind := value.Kind()
	if kind == reflect.Ptr {
		return GetRealValue(value.Elem())
	}
	if kind == reflect.Interface {
		// eg:var s2 interface{}
		//	s2 = User{}
		//	fmt.Println(reflect.ValueOf(&s2).Elem().Kind())// interface
		// So we need to convert it here
		if value.CanInterface() {
			return GetRealValue(reflect.ValueOf(value.Interface()))
		}
		return GetRealValue(value.Elem())
	}
	return value
}
Copy the code

Solve the problem and get started

2. Underline naming

How to solve the underscore, struct field belongs to the hump nomenclature, how to solve, for this.

I wrote a simple utility class

Problem: 1. If ID is used, print i_d

Unsafe, because an array to a slice needs to be copied once, you can use unsafe to address issues such as the underlying string being sliced

func CamelCase(s string) string {
	if s == "" {
		return ""
	}
	t := make([]byte.0.32)
	i := 0
	for ; i < len(s); i++ {
		c := s[i]
		if isASCIIDigit(c) {
			t = append(t, c)
			continue
		}
		if isASCIIUpper(c) {
			c ^= ' '
		}
		t = append(t, c)
		for i+1 < len(s) && isASCIIUpper(s[i+1]) {
			i++
			t = append(t, '_', s[i]+32)}}//return *(*string)(unsafe.Pointer(&t))
	return string(t)
}
func isASCIIUpper(c byte) bool {
	return 'A' <= c && c <= 'Z'
}

func isASCIIDigit(c byte) bool {
	return '0' <= c && c <= '9'
}
Copy the code

3, open dry

1. Solve the time problem

2, reflection, underline naming

func ToStdMap(bean interface{}) map[string]interface{} {
	_value := GetRealValue(reflect.ValueOf(bean))
	if_value.Kind() ! = reflect.Struct {panic("the bean mush struct")
	}
	_type := _value.Type()
	fieldNum := _value.NumField()
	_map := make(map[string]interface{}, fieldNum)
	for x := 0; x < fieldNum; x++ {
		field := _type.Field(x)
		value := GetRealValue(_value.Field(x))
		if value.CanInterface() {
			realValue := value.Interface()
			switch realValue.(type) {
			case time.Time:
				_map[CamelCase(field.Name)] = times.FormatStdTime(realValue.(time.Time))
			default:
				_map[CamelCase(field.Name)] = realValue
			}
		}
	}
	return _map
}
Copy the code

4, test,

func TestObjToMap(t *testing.T) {
	users := Users{
		UserName: "xiaoli",
	}
	now := time.Now()
	users.CreateTime = &now
	stdMap := ToStdMap(users)
	bytes, err := json.Marshal(stdMap)
	iferr ! =nil {
		t.Fatal(err)
	}
	fmt.Printf("%s\n", bytes)
}
Copy the code

Output result:

Perfect, the defect is the need to use likedMap, because Golang source package does not have, so 😓, destined to disorder

{"create_time":"The 2020-06-05 14:05:31"."user_name":"xiaoli"}
Copy the code

3. Using unsafe for direct operations

The following are two objects with the same structure. According to unsafe, an object must be larger than or equal to the structure of an object B (requiring a consistent array) to convert from a to B

type UserModel struct { // Database object
	Name     string
	Age      int
	Birthday time.Time
}
type UserDto struct { // Data transfer object, also requires that the data out of the format is a standard format
	Name     string `json:"name"`
	Age      int    `json:"age"`
	Birthday Time   `json:"birthday"`
}

type Time time.Time
func (this Time) MarshalText(a) (text []byte, err error) {
	i := time.Time(this)
	return []byte(i.Format("The 2006-01-02 15:04:05")), nil
}
Copy the code

code

func BenchmarkName(b *testing.B) {
	for i := 0; i < b.N; i++ {
		model := UserModel{
			Name:     "tom",
			Age:      1,
			Birthday: time.Now(),
		}
		userDao := (*UserDto)(unsafe.Pointer(&model))
		_, _ = json.Marshal(userDao)
		//fmt.Println(string(bytes))
    //// {"name":"tom","age":1,"birthday":"2020-06-26 15:20:20"}}}Copy the code

This code is too violent to convert a->b directly, and does not need to open up new memory, so it can be used when a==b.