在使用 go 语言开发过程中,经常需要使用到 json 包来进行 json 和 struct 的互相转换,在使用过程中,遇到了一些需要额外注意的地方,记录如下。
整数变浮点数问题
假设有一个 Person 结构,其中包含 Age int64 和 Weight float64 两个字段,现在通过 json 包将 Person 结构转为 map[string]interface{},代码如下。
type Person struct {
Name string
Age int64
Weight float64
}
func main() {
person := Person{
Name: "Wang Wu",
Age: 30,
Weight: 150.07,
}
jsonBytes, _ := json.Marshal(person)
fmt.Println(string(jsonBytes))
var personFromJSON interface{}
json.Unmarshal(jsonBytes, &personFromJSON)
r := personFromJSON.(map[string]interface{})
}
代码执行到这里看上去一切正常,但是打印一下 map[string]interface{} 就会发现不太对了。
fmt.Println(reflect.TypeOf(r["Age"]).Name()) // float64 fmt.Println(reflect.TypeOf(r["Weight"]).Name()) // float64
转换成 map[string]interface{} 之后,原先的 uint64 和 float64 类型都被转换成了 float64 类型,这显然是不符合我们的预期的。

查看 json 的规范可以看到,在 json 中是没有整型和浮点型之分的,所以现在可以理解 json 包中的 Unmarshal 方法转出的数字类型为什么都是 float64 了,因为根据 json 规范,数字都是同一种类型,那么对应到 go 的类型中最接近的就是 float64 了。
json 包还针对这个问题提供了更好的解决方案,不过需要使用 json.Decoder 来代替 json.Unmarshal 方法,将 json.Unmarhsal 替换如下。
var personFromJSON interface{}
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
decoder.UseNumber()
decoder.Decode(&personFromJSON)
r := personFromJSON.(map[string]interface{})
这种方法首先创建了一个 jsonDecoder,然后调用了 UseNumber 方法,从文档中可以知道,使用 UseNumber 方法后,json 包会将数字转换成一个内置的 Number 类型(而不是 float64),这个 Number 类型提供了转换为 int64、float64 等多个方法。
时间格式
对于 json 格式,是没有时间类型的,日期和时间以 json 格式存储时,需要转换为字符串类型。这就带来了一个问题,日期时间的字符串表示有多种多样,go 的 json 包支持的是哪一种呢?
使用下面的代码来输出 json.Marshal 方法将 Time 类型转换为字符串后的格式。
type Person struct {
Name string
Birth time.Time
}
func main() {
person := Person{
Name: "Wang Wu",
Birth: time.Now(),
}
jsonBytes, _ := json.Marshal(person)
fmt.Println(string(jsonBytes)) // {"Name":"Wang Wu","Birth":"2018-12-20T16:22:02.00287617+08:00"}
}










