深圳幻海软件技术有限公司 欢迎您!

Go开发中结构体 model、dto 、time格式问题

2023-02-28

1、背景model层不允许使用json,dto层又重复造轮子,一个表的字段可能20个左右,那么赋值语句难受死了。其次就是json直接解析,model层的time.Time,完蛋格式不对,返回的数据不对。比如 复制{     "user_na

1、背景

model层不允许使用 json, dto层又重复造轮子,一个表的字段可能20个左右,那么赋值语句难受死了。

其次就是json直接解析,model层的time.Time,完蛋格式不对,返回的数据不对。

比如

 


    "user_name""xiaoli"
    "create_time""2020-06-05T13:53:06.293614+08:00" 

  • 1.
  • 2.
  • 3.
  • 4.

这种情况,无法解决,就需要必须重写一个dto。

那么如何解决这个问题呢,本人思考了一段时间,最终使用Map来解决。

2、解决问题

1、反射

那么反射会遇到,各种奇葩的书写方式,有些人什么都出传入指针,有些人各种interface{} 隐藏转换,反正就是太过于差异化。

所以就是需要解决,如何准确的拿到Value对象,下面是我写的一个工具类

 

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 
        // 所以这里需要将它转换 
        if value.CanInterface() { 
            return GetRealValue(reflect.ValueOf(value.Interface())) 
        } 
        return GetRealValue(value.Elem()) 
    } 
    return value 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

解决这个问题,开干

2、下划线命名法

下划线如何解决,结构体的字段属于驼峰命名法,怎么解决呢?

写了一个简单的工具类

问题:

  • 如果是ID,连续大写,输出i_d
  • 因为数组到切片需要拷贝一次,所以可以利用unsafe解决,因为字符串底层就是切片,但是不安全

 

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' 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

3、开干

  • 解决time的问题
  • 反射、下划线命名法

 

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 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

4、测试

 

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

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

输出结果:

完美,美中不足是需要使用likedMap,由于Golang源码包没有,所以,注定乱序

{"create_time":"2020-06-05 14:05:31","user_name":"xiaoli"
  • 1.