起因

Java 中的 Timestamp 转换为 JSON 是长整型,但在 Golang 中,却是完全不同。因为需要将 Java 的代码转换为 Golang,所以遇到这种相互转换的问题。在 Golang中,默认的转换如下:

package main

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

// Person : for test struct
type Person struct {
	Birthday time.Time `json:"birthday"`
}

func main() {
	p := Person{Birthday: time.Date(1991, time.August, 01, 0, 0, 0, 0, time.UTC)}
	j, _ := json.Marshal(p)
	fmt.Printf("json := %v\n", string(j))
}

输出如下:

[Running] go run "/private/tmp/goooo/src/main.go"
json := {"birthday":"1991-08-01T00:00:00Z"}

[Done] exited with code=0 in 0.271 seconds

解决

因为在 Java 中,它输出的是 long 类型(毫秒)。所以, 要自定义格式化输入输出 JSON 转换了。不过还好, Golang 中可以自定义这种格式。它提供两个接口:

// JavaTime : time.Time
type JavaTime time.Time

// UnmarshalJSON : UnmarshalJSON 自定义从json->自定义类型
func (j *JavaTime) UnmarshalJSON(data []byte) error {
	millis, err := strconv.ParseInt(string(data), 10, 64)
	if err != nil {
		return err
	}
	*j = JavaTime(time.Unix(0, millis*int64(time.Millisecond)))
	return nil
}

// MarshalJSON : 自定义类型转换到 json
func (j *JavaTime) MarshalJSON() (data []byte, err error) {
	var buf bytes.Buffer
	origin := time.Time(*j)
	buf.WriteString(strconv.FormatInt(origin.UnixNano()/int64(time.Millisecond), 10))
	return buf.Bytes(), nil
}

MarshalJSON : 从类型转换为 json 字符串 UnmarshalJSON : 从json 字符串转换为类型

这样子, 就可以将 Java中的 Timestamp 类型转换为 Golang 中的 time.Time 类型了。

完整例子

package main

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

// Person : for test struct
type Person struct {
	Birthday *JavaTime `json:"birthday"`
}

func main() {
	bir := JavaTime(time.Date(1991, time.August, 01, 0, 0, 0, 0, time.UTC))
	p := Person{Birthday: &bir}

	// 从类型转换为 json 字符串:
	j, _ := json.Marshal(p)
	fmt.Printf("json := %v\n", string(j))

	// 从 json 字符串,转换为 类型:
	json.Unmarshal(j, &p)
	bir = *p.Birthday

	golangTime := time.Time(bir)
	fmt.Printf("bir year = %v\n", golangTime.Year())
}

// JavaTime : time.Time
type JavaTime time.Time

// UnmarshalJSON : UnmarshalJSON 自定义从json->转换器
func (j *JavaTime) UnmarshalJSON(data []byte) error {
	millis, err := strconv.ParseInt(string(data), 10, 64)
	if err != nil {
		return err
	}
	*j = JavaTime(time.Unix(0, millis*int64(time.Millisecond)))
	return nil
}

// MarshalJSON : 自定义对象转换到 json
func (j *JavaTime) MarshalJSON() (data []byte, err error) {
	var buf bytes.Buffer
	origin := time.Time(*j)
	buf.WriteString(strconv.FormatInt(origin.UnixNano()/int64(time.Millisecond), 10))
	return buf.Bytes(), nil
}

输出如下:

[Running] go run "/private/tmp/goooo/src/main.go"
json := {"birthday":681004800000}
bir year = 1991

[Done] exited with code=0 in 0.325 seconds

注意事项

类型别名 与 原类型的转换

如上面的 type JavaTime time.Time

JavaType 转换为 time.Time : time.Time(JavaTime) time.Time 转换为 JavaTime : JavaTime(time.Time)

使用

可以看到,Birthday *JavaTime, 它使用的是指针的形式。不然,无法正常转换。

因为我们的那两个接口的接收者为(j *JavaTime)

可以自行将这两个方法, 将接收者去掉指针的形式的话, 转换的结果如何。我这时测试的是Go 1.7,发现只能指针的形式及指针的使用才能正常转换。看网上的资料的, 好像将MarshalJSON的接收者不为指针的形式也可以。这个我也没有测试过,因为只想使用最新版的 Go, ^_^.