go自定义json

go自定义json,CustomMarshalJSON

需求描述: golang 的原生 json package 有时会有一些与预期不符合的情况,
例如对接编码(json.Marshal) golang 会默认 “整型浮点数” 如: 1.00 转换为json 的整型 1, 但有时并不希望这种转换.
所以就有了CustomMarshalJSON,即自定义json


官方例子

Marshaler 和 Unmarshaler 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 
// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler interface {
MarshalJSON() ([]byte, error)
}

// Unmarshaler is the interface implemented by types
// that can unmarshal a JSON description of themselves.
// The input can be assumed to be a valid encoding of
// a JSON value. UnmarshalJSON must copy the JSON data
// if it wishes to retain the data after returning.
//
// By convention, to approximate the behavior of Unmarshal itself,
// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}

你只要能看懂一个单词themselves,你就明白该怎么解决上面需求描述里的问题了。

例1:

解决浮点数据类型编码小数位丢失的问题

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"time"
)

type Person struct {
Name string
Util float64
}

type StrictFloat64 float64

// 自定义类型上实现 Marshaler 的接口, 在进行 Marshal 时就会使用此除的实现来进行 json 编码
func (f StrictFloat64) MarshalJSON() ([]byte, error) {
if float64(f) == float64(int(f)) {
return []byte(strconv.FormatFloat(float64(f), 'f', -1, 64)), nil // 可以自由调整精度
}
return []byte(strconv.FormatFloat(float64(f), 'f', -1, 64)), nil
}

type StrictPerson struct {
Name string
Util StrictFloat64
}

func main() {
p1 := Person{"ross", 1.01}
p2 := Person{"jack", 1.00}
p1Json, _ := json.Marshal(p1)
fmt.Println("\"ross\", 1.01 --> " + string(p1Json))
p2Json, _ := json.Marshal(p2)
fmt.Println("\"ross\", 1.00 --> " + string(p2Json))

sp1 := StrictPerson{"ross", 1.01}
sp2 := StrictPerson{"jack", 1.00001}
sp1Json, _ := json.Marshal(sp1)
fmt.Println("\"ross\", 1.01 (自定义 StrictFloat64 类型)--> " + string(sp1Json))
sp2Json, _ := json.Marshal(sp2)
fmt.Println("\"ross\", 1.00 (自定义 StrictFloat64 类型) --> " + string(sp2Json))

p_Demo := &StrictPerson{}
json.Unmarshal(sp2Json, p_Demo)
fmt.Printf("%#v", p_Demo)
}

// 输出:
//"ross", 1.01 --> {"Name":"ross","Util":1.01}
//"ross", 1.00 --> {"Name":"jack","Util":1}
//"ross", 1.01 (自定义 StrictFloat64 类型)--> {"Name":"ross","Util":1.01}
//"ross", 1.00 (自定义 StrictFloat64 类型) --> {"Name":"jack","Util":1.00001}
//&main.StrictPerson{Name:"jack", Util:1.00001}

例2:

解决非2 RFC3339 标准格式时间转换问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type ExTime struct {
time.Time
}

func (t *ExTime) UnmarshalJSON(b []byte) error {
b = bytes.Trim(b, "\"") // 此除需要去掉传入的数据的两端的 ""
ext, err := time.Parse(ExTimeUnmarshalTimeFormat, string(b))
if err != nil {
// do something
}
*t = ExTime{ext}
return nil
}

func (t ExTime) MarshalJSON() ([]byte, error) {
var stamp = fmt.Sprintf("\"%s\"", time.Time(t.Time).Format(ExTimeMarshalTimeFormat))
return []byte(stamp), nil
}

var ExTimeUnmarshalTimeFormat = "2006-01-02 15:04:05"
var ExTimeMarshalTimeFormat = "2006-01-02 15:04:05"
例3:

解决内嵌结构体序列化问题

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
32
33
34
35
36
37
type Message struct {
From string `json:"from"`
To string `json:"to"`
Data string `json:"data"`
}

func main() {
msg := Message{
From: "XiaoMing",
To: "LiGang",
Data: `{"title":"test","body":"something"}`,
}
jsonData, err := json.Marshal(msg)
if err != nil {
panic(err)
}
fmt.Println(string(jsonData))
}
// 在上面的例子中,Data字段是string类型,但是保存的内容是json格式的数据,这个时候,程序输出:
// {"from":"XiaoMing","to":"LiGang","data":"{\"title\":\"test\",\"body\":\"something\"}"}
// 序列化之后的data是一个字符串,而不是我们想要的json结构的字符串

// 解决办法:

type JsonString string

func (j JsonString) MarshalJSON() ([]byte, error) {
fmt.Println("marshal...")
return []byte(j), nil
}

type Message2 struct {
From string `json:"from"`
To string `json:"to"`
Data JsonString `json:"data"`
}