Go 标准库的使用

golang.jpg

俗话说,”工欲善其事,必先利其器”,要想掌握好一门语言,就得先熟练应用它的标准库。

Time

1. Now

Go内置了time模块提供操作时间日期的接口,Now方法返回一个time.Time类型数据,可以通过下列的方法得到年、月、日等具体数值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var now time.Time = time.Now()

fmt.Println(now)
fmt.Println(now.Year())
fmt.Println(now.Month())
fmt.Println(now.Day())
fmt.Println(now.Hour())
fmt.Println(now.Minute())
fmt.Println(now.Second())
fmt.Println(now.Date())

/*
2019-03-27 15:59:08.1299297 +0800 CST m=+0.002992001
2019
March
27
15
59
8
2019 March 27
*/

2. 时间戳

得到了time.Time类型数据之后,可以很简单地通过UnixUnixNano方法得到秒级和毫秒级的时间戳:

1
2
3
4
5
fmt.Println("Unix timestamp: ", now.Unix())
fmt.Println("UnixNano timestamp: ",now.UnixNano())

// Unix timestamp: 1553673548
// UnixNano timestamp: 1553673548129929700

将时间戳转换成time.Time也很简单:

1
2
timestamp := 1553660765
fmt.Println(time.Unix(timestamp, 0).UTC())

3. 时间字符串

Go中显示的布局有个默认值即2006-01-02 15:04:05

1
2
3
4
5
6
7
8
9
10
var defaultStringFormat = "2006-01-02 15:04:05"

now := time.Now()
// 时间和字符串的相互转换
fmt.Println("Datetime string: ", now.Format(defaultStringFormat))

fmt.Println(time.Parse(defaultStringFormat, "2019-03-27 12:28:14"))

// Datetime string: 2019-03-27 16:06:49
// 2019-03-27 12:28:14 +0000 UTC <nil>

Json

1. 标签

在定义结构体时,可以为JSON的序列化设置标签,如下:

1
2
3
4
5
6
7
type User struct {
Name string `json:"user, omitempty"`
Password string `json:"password"`
Age int `json:"age"`

Profile Profile `json:"-"`
}
  • json 内定义了结构体序列化时显示的字段名;
  • omitempty 表示该字段为空时,不序列化
  • - 表示忽略该字段

2. 序列化

序列化通过Marshal可以将结构体转换为JSON字符串,注意jsonResult[]byte类型:

1
2
3
4
func (user *User) toJson() string {
jsonResult, _ := json.Marshal(user)
return string(jsonResult)
}
1
2
3
4
5
6
7
8
func test() {
profile := Profile{"123", "123456"}
user := User{Name: "Pushy", Password: "Hello", Age: 20, Profile: profile,}

fmt.Println(user.toJson())
}

// {"user":"Pushy","password":"Hello","age":20,"Profile":{"AvatarUrl":"123","Openid":"123456"}}

3. 反序列化

反序列化通过Unmarshal方法可以将JSON字符串转换为结构体类型,同样Unmarshal传入也必须是[]byte类型数据:

1
2
3
4
5
6
7
8
9
func test() {
jsonString := `{"user":"Pushy","password":"Hello","age":20,"Profile":{"AvatarUrl":"123","Openid":"123456"}}`

var user User
err := json.Unmarshal([]byte(jsonString), user)
if err != nil {
fmt.Println("Parse error")
}
}

4. 其他方法

Valid

用来校验是否是有效的JSON数据:

1
2
3
4
5
6
7
func ValidJson() {
errorJsonString := `{message: "hello world"`
res := json.Valid([]byte(errorJsonString))
fmt.Println(res)
}

// false

Marshaler接口

Marshaler是一个接口,它定义了MarshalJSON方法用来自定义序列化返回值:

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

例如对于下列的结构体:

1
2
3
4
5
6
7
8
9
type Post struct {
Title string `json:"title"`
CreateTime time.Time `json:"createTime"`
}

func (post *Post) toJsonString() string {
jsonResult, _ := json.Marshal(post)
return string(jsonResult)
}

当序列化时我们希望自己自定义时间日期的格式,而不是像下面输出的那样:

1
2
3
4
5
6
func test() {
post := Post{"Hello World", time.Now()}
fmt.Println(post.toJsonString())
}

// {"title":"Hello World","createTime":"2019-03-27T12:10:02.9227866+08:00"}

这时候Marshaler就可以排上用场了,修改结构体的定义,并实现Marshaler接口,在MarshalJSON中返回自定义的转换格式:

1
2
3
4
5
6
7
8
9
10
11
12
type jsonTime time.Time

type Post struct {
Title string `json:"title"`
CreateTime jsonTime `json:"createTime"`
}

func (this jsonTime) MarshalJSON() ([]byte, error) {
// 自定义时间日期的格式化格式
var stamp = fmt.Sprintf("\"%s\"", time.Time(this).Format("2006-01-02 15:04:05"))
return []byte(stamp), nil
}

这样,序列化出的JSON字符串就是我们自定义的时间日期格式了:

1
2
3
4
5
6
func test() {
post := Post{"Hello World", jsonTime(time.Now())}
fmt.Println(post.toJsonString())
}

// {"title":"Hello World","createTime":"2019-03-27 12:15:19"}

error

1. 基本使用

Go内部提供了error接口,仅定义了一个Error方法,返回字符串类型的错误信息:

1
2
3
type error interface {
Error() string
}

我们可以通过errors.New或者fmt.Errorf来创建error错误:

1
2
3
4
5
6
7
8
9
10
func foo(x int) error {
if x == 0 {
//err := errors.New("The x can't be 0")
err := fmt.Errorf("%s", "The x can't be 0")
return err
} else {
fmt.Println(x + 1)
return nil
}
}

下面的异常处理机制只是简单地打印出错误类型,这是Go官方所推荐的做法:

1
2
3
4
5
6
7
8
func main() {
err := foo(0)
if err != nil {
fmt.Println(err.Error())
}
}

// The x can't be 0

除此之外,还可以通过panic方法来强制抛出异常(和Java中的运行时异常类似),这种异常的处理将会使线程挂掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
err := foo(0)
if err != nil {
panic(err)
}
}


/*
panic: The x can't be 0

goroutine 1 [running]:
main.main()
C:/Users/Pushy/go/src/helloworld/error/usage.go:22 +0x5e
*/

2. 自定义异常

我们可以实现error接口的Error方法来自定义异常,在Error方法返回自定义错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 自定义错误
type CodeError struct {
Code int
Message string
}

// 实现Error接口
func (c CodeError) Error() string {
return fmt.Sprintf("The error code is %d, message is %s", c.Code, c.Message)
}

func foo() error {
codeErr := CodeError{400, "Bad request"}
return codeErr
}

func main() {
err := foo()
if err != nil {
fmt.Println(err.(CodeError).Message)
}
}

3. try-catch

虽然Go中没有和Java等语言一样的try-catch关键字语句,但是可以通过deferrecover来实现同样的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func catch() {
// 调用recover进行恢复
if e := recover(); e != nil {
fmt.Println("recover success.")
}
}

func main() {
err := function()
defer catch()

if err != nil {
fmt.Println(err.(CodeError).Message)
// panic退出程序会先处理完当前goroutine已经defer挂上去的任务
// 也就是说当panic执行之后,会首先执行catch方法,而不会立即结束当前goroutine
panic(err)
}
}
Pushy wechat
欢迎订阅我的微信公众号