kagxin / blog

个人博客:技术、随笔、生活
https://github.com/kagxin/blog/issues
7 stars 0 forks source link

golang database/sql 的valuer、scanner接口和encoding/json 的MarshalJSON和UnmarshalJSON #65

Open kagxin opened 3 years ago

kagxin commented 3 years ago

schme

database/sql的valuer和scanner

type Scanner interface {
    Scan(src interface{}) error
}
type Valuer interface {
    Value() (Value, error)
}

import ( "database/sql" "database/sql/driver" "fmt" "log" "time"

_ "github.com/go-sql-driver/mysql"

)

// LAYOUT 日期格式 var LAYOUT = "2006-01-02 15:04:05"

// Foo 表结构对应结构体 type Foo struct { ID int gorm:"column:id" CreateTime MyTime gorm:"column:create_time" }

// MyTime 自定义日期类型存时间戳 type MyTime int64

// Scan 数据库类型装换为MyTime类型 func (t MyTime) Scan(src interface{}) error { switch src.(type) { // 将字符串byte装换为MyTime类型 case []byte: tm, err := time.ParseInLocation(LAYOUT, string(src.([]byte)), time.Local) t = MyTime(tm.Unix()) return err } return nil }

// Value MyTime转换为数据库类型 func (t MyTime) Value() (driver.Value, error) { // 将MyTime类型转换为layout 格式的字符串byte return []byte(time.Unix(int64(t), 0).Format(LAYOUT)), nil }

func main() { var ( err error f Foo r Foo ) db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1)/demo?timeout=10s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8mb4,utf8") if err != nil { panic(err) } defer db.Close() err = db.Ping() if err != nil { log.Fatalln("ping db fail:", err) } // 插入数据库 t1 := MyTime(time.Now().Unix()) f = Foo{CreateTime: t1} _, err = db.Exec("insert into t_table (create_time) values (?)", f.CreateTime) if err != nil { log.Fatal(err) } fmt.Printf("insert create time: %d\n", int64(t1))

// 查询数据库
err = db.QueryRow("select id, create_time from t_table order by id desc limit 1").Scan(&r.ID, &r.CreateTime)
if err != nil {
    log.Fatal(err)
    return
}
fmt.Printf("query create time %d\n", int64(r.CreateTime))

}

### 输出打印和数据库值
```output
insert create time: 1601370897
query create time 1601370897
数据库内容 id create_time
1 2020-09-29 17:14:57

encoding/json 的MarshalJSON和UnmarshalJSON

type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

type Unmarshaler interface {
    UnmarshalJSON([]byte) error
}

通过MarshalJSON和UnmarshalJSON 自定义golang类型的序列化和反序列化的行为

  • MarshalJSON将golang对象序列化为json []byte
  • UnmarshalJSON 将json []byte 转化为golang对象

示例

golang time.Time是以RFC3339的格式序列化和反序列化,示例代码重新定义time.Time的序列化和反序列化行为,时间戳(json)和自定义MyTime(golang类型)类型的转化。

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "strconv"
    "time"
)

// MyTime 自定义时间类型
type MyTime time.Time

// MarshalJSON MyTime 的序列化方法,转化为时间戳
func (t MyTime) MarshalJSON() ([]byte, error) {
    timeStramp := time.Time(t).Unix()
    return []byte(strconv.Itoa(int(timeStramp))), nil
}

// UnmarshalJSON 时间戳[]byte 转化为MyTime类型
func (t *MyTime) UnmarshalJSON(data []byte) error {
    timeStramp, err := strconv.Atoi(string(data))
    if err != nil {
        return err
    }
    *t = MyTime(time.Unix(int64(timeStramp), 0))
    return nil
}

// T asdf
type T struct {
    CreateTime MyTime `json:"create_time"`
}

func main() {
    tString, err := json.Marshal(T{CreateTime: MyTime(time.Now())})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("序列化得到的json string: %s\n", string(tString))
    var t = &T{}
    if err := json.Unmarshal(tString, t); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("反序列化的gloang结构体: %#v\n", t)
}

输出打印

序列化得到的json string: {"create_time":1601433814}
反序列化的gloang结构体: &main.T{CreateTime:main.MyTime{wall:0x0, ext:63737030614, loc:(*time.Location)(0x11e7580)}}
kagxin commented 3 years ago

TODO: