encoding/json
反序列化时的数值处理及float64精度问题
encoding/json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import "fmt"
import "encoding/json"
func test_std_json(){
var m []interface{}
if err := json.Unmarshal([]byte(`[100, null, 1.2, "1234"]`), &m); err == nil {
for _, v := range m {
fmt.Printf("type: %T, value: %v\n",v, v )
}
} else {
fmt.Println("Unmarshal error: ", err)
}
}
调用上面的函数会输出:
1
2
3
4
type: float64, value: 100一般情况下是不会有什么问题的,但是,如果我们传进去的是一个大整型(超过float64定义的范围),那么就粗大事了。举个栗子:
type: <nil>, value: <nil>
type: float64, value: 1.2
type: string, value: 1234
1
2
3
4
5
6
7
8
9
10
11
12
func test_std_json_large(){调用上面的函数会输出:
var m []interface{}
if err := json.Unmarshal([]byte(`[100, 1234567890123456789, null, 1.2, "1234"]`), &m); err == nil {
for _, v := range m {
fmt.Printf("type: %T, value: %v\n",v, v )
}
} else {
fmt.Println("Unmarshal error: ", err)
}
}
1
2
3
4
5
type: float64, value: 100注意到了吗?上面的数字
type: float64, value: 1.2345678901234568e+18
type: <nil>, value: <nil>
type: float64, value: 1.2
type: string, value: 1234
1234567890123456789
是一个在int64范围内,但是在float64之外的数值。反序列化之后,这个值变成了123456789012345678
!!!试想一下,本来你手头有1234567890个亿,经过json.Unmarshal
,就只剩123456789个亿了┭┮﹏┭┮
但是没关系,此事并非不可解。下面我们来看看两种解决方案。
方法一:使用标准库的json.Decoder
json.NumberDecoder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func test_std_json_large_withNumber(){调用上面的函数会输出:
var m []interface{}
decoder := json.NewDecoder(strings.NewReader(`[100, 1234567890123456789, null, 1.2, "1234"]`))
decoder.UseNumber()
if err := decoder.Decode(&m); err == nil {
for _, v := range m {
fmt.Printf("type: %T, value: %v\n",v, v )
}
} else {
fmt.Println("Unmarshal error: ", err)
}
}
1
2
3
4
5
type: json.Number, value: 100这样,我们的1234567890个亿还是1234567890个亿。可以把
type: json.Number, value: 1234567890123456789
type: <nil>, value: <nil>
type: json.Number, value: 1.2
type: string, value: 1234
json.Number
当成字符串,标准库对于这个类型还提供了一些方便的方法来取出数值。具体可以参考json.Number
以上的代码在:这里
type Number stringtypeNumberstringNumber
方法二:换个库吧
jsoniter是国人写的一个用来替代标准库的json库。这个库允许我们自定义类型解析函数。在其github中的某个issue中提到优先把数值当成int64处理的方法。以下代码注册了一个类型解析函数,这个函数会首先尝试把数值解析成int64,解析失败则将其当成float64处理:
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
import (
"encoding/json"
"github.com/json-iterator/go"
"strconv"
"unsafe"
)
func init() {
decodeNumberAsInt64IfPossible := func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
switch iter.WhatIsNext() {
case jsoniter.NumberValue:
var number json.Number
iter.ReadVal(&number)
i, err := strconv.ParseInt(string(number), 10, 64)
if err == nil {
*(*interface{})(ptr) = i
return
}
f, err := strconv.ParseFloat(string(number), 64)
if err == nil {
*(*interface{})(ptr) = f
return
}
// Not much we can do here.
default:
*(*interface{})(ptr) = iter.Read()
}
}
jsoniter.RegisterTypeDecoderFunc("interface {}", decodeNumberAsInt64IfPossible)
}
Unmarshal
当一切成空,序列化时该怎么办
官方文档有云:
1
2
3
a nil slice encodes as the null JSON value.举个例子:
A nil pointer encodes as the null JSON value.
A nil interface value encodes as the null JSON value.
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
package main输出:
import (
"fmt"
"encoding/json"
)
func main() {
// map
var sm map[string]interface{}
sb, err := json.Marshal(sm)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
// slice
var ss []string
sb, err = json.Marshal(ss)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
var sbs []byte
sb, err = json.Marshal(sbs)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
// pointer
var sp *string
sb, err = json.Marshal(sp)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
// interface
var si interface{}
sb, err = json.Marshal(si)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
}
1
2
3
4
5
null, [110 117 108 108], err: <nil>注意哦,这里序列化得到的结果并不是大小为0的byte切片,而是字符串
null, [110 117 108 108], err: <nil>
null, [110 117 108 108], err: <nil>
null, [110 117 108 108], err: <nil>
null, [110 117 108 108], err: <nil>
null
!!!当然,如果不想输出字符串null
,那么可以修改为: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main输出:
import (
"fmt"
"encoding/json"
)
func main() {
// map
sm := make(map[string]interface{}, 0)
sb, err := json.Marshal(sm)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
// slice
ss := make([]string, 0)
sb, err = json.Marshal(ss)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
// bytes
sbs := make([]byte, 0)
sb, err = json.Marshal(sbs)
fmt.Printf("%s, %v, err: %v\n", string(sb), sb, err)
}
1
2
3
{}, [123 125], err: <nil>此时,输出分别是对应类型的零值的字符串表示。例如,空 Map 的序列化值为字符串
[], [91 93], err: <nil>
"", [34 34], err: <nil>
{}
所以,如果你只是希望在空的情况下,序列化得出空的结果,那么最好在序列化之前进行一次判空。