interface struct 的 json 反序列化

通过创建临时结构体实现 struct 内 interface struct 的 json 反序列化

背景

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
type AData struct {
	A string `json:"a"`
}

type BData struct {
	B string `json:"b"`
}

type Message struct {
	Name string      `json:"name"`
	Id   int         `json:"id"`
	Data interface{} `json:"data"`
}

对于 interface 类型的数据很容易实现序列化(不需要任何额外步骤)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
msgA := Message{
	Name: "msg_a",
	Id:   1,
	Data: AData{
		A: "a_data",
	},
}
msgB := Message{
	Name: "msg_b",
	Id:   2,
	Data: BData{
		B: "b_data",
	},
}

msgAJ, _ := json.Marshal(msgA)
log.Info("A", zap.Reflect("msgA", msgA), zap.ByteString("msgAJ", msgAJ))

msgBJ, _ := json.Marshal(msgB)
log.Info("B", zap.Reflect("msgB", msgB), zap.ByteString("msgBJ", msgBJ))

interface 反序列化后会变成 map[string]interface 类型,想要转成 struct 只能使用 mapstructure 之类的库

1
2
3
4
var msgX Message
_ = json.Unmarshal(msgAJ, &msgX)
log.Info("X", zap.Reflect("msgX", msgX), zap.Reflect("msgX.Data.A", msgX.Data.(AData).A))
// panic: interface conversion: interface {} is map[string]interface {}, not main.AData

此处是无法直接用 msgX.Data.A 来访问的,同样的 msgX.Data.(AData).A 也是不行的,因为这时候的 data 已经被反序列化成了 map[string]interface

解决方法 1

解决方法也很简单,只要再反序列化时能够知道需要反序列化成的类型即可。

在解析的时候定义临时 struct 继承 Message 并重新定义 Data 的类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
msgXA := struct {
	*Message
	Data AData `json:"data"`
}{}
_ = json.Unmarshal(msgAJ, &msgXA)
log.Info("XA", zap.Reflect("msgXA", msgXA), zap.Reflect("msgXA.Data.A", msgXA.Data.A))

msgXB := struct {
	*Message
	Data BData `json:"data"`
}{}
_ = json.Unmarshal(msgBJ, &msgXB)
log.Info("XB", zap.Reflect("msgXB", msgXB), zap.Reflect("msgXB.Data.B", msgXB.Data.B))

解决方法 2

另一种思路是拆分 struct 每次序列化时将其合并,反序列化时再将其拆分12

缺点是每次需要传送两个变量

 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
type ShortMessage struct {
	Name string `json:"name"`
	Id   int    `json:"id"`
}

func TestJsonStructSplit(t *testing.T) {
	msgA := ShortMessage{
		Name: "msg_a",
		Id:   1,
	}
	dataA := AData{
		A: "a_data",
	}

	msgB := ShortMessage{
		Name: "msg_b",
		Id:   2,
	}
	dataB := BData{
		B: "b_data",
	}

	// marshal

	msgAJ, _ := json.Marshal(struct {
		*ShortMessage
		*AData
	}{&msgA, &dataA})

	msgBJ, _ := json.Marshal(struct {
		*ShortMessage
		*BData
	}{&msgB, &dataB})

	// unmarshal

	var msgXA ShortMessage
	var dataXA AData
	_ = json.Unmarshal(msgAJ, &struct {
		*ShortMessage
		*AData
	}{&msgXA, &dataXA})

	var msgXB ShortMessage
	var dataXB BData
	_ = json.Unmarshal(msgBJ, &struct {
		*ShortMessage
		*BData
	}{&msgXB, &dataXB})
}

解决方法 3

只在反序列化时拆分12

缺点是 Data 实际上被解析了两次(一次解析成了 map,另一次解析成了 struct),而且每次使用时候还必须进行类型转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
var msgXA Message
var dataXA AData
_ = json.Unmarshal(msgAJ, &struct {
	*Message
	*AData `json:"data"`
}{&msgXA, &dataXA})
msgXA.Data = dataXA
t.Log("msgXA", msgXA, "data", msgXA.Data.(AData).A)

var msgXB Message
var dataXB BData
_ = json.Unmarshal(msgBJ, &struct {
	*Message
	*BData `json:"data"`
}{&msgXB, &dataXB})
msgXB.Data = dataXB
t.Log("msgXB", msgXB, "data", msgXB.Data.(BData).B)

完整测试代码

  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
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main

import (
	"encoding/json"
	"testing"
)

type AData struct {
	A string `json:"a"`
}

type BData struct {
	B string `json:"b"`
}

type Message struct {
	Name string      `json:"name"`
	Id   int         `json:"id"`
	Data interface{} `json:"data"`
}

var msgA = Message{
	Name: "msg_a",
	Id:   1,
	Data: AData{
		A: "a_data",
	},
}

var msgB = Message{
	Name: "msg_b",
	Id:   2,
	Data: BData{
		B: "b_data",
	},
}

func TestJsonStruct(t *testing.T) {
	// marshal

	msgAJ, _ := json.Marshal(msgA)
	msgBJ, _ := json.Marshal(msgB)

	// unmarshal

	msgXA := struct {
		*Message
		Data AData `json:"data"`
	}{}
	_ = json.Unmarshal(msgAJ, &msgXA)
	t.Log("msgXA", msgXA, "data", msgXA.Data.A)

	msgXB := struct {
		*Message
		Data BData `json:"data"`
	}{}
	_ = json.Unmarshal(msgBJ, &msgXB)
	t.Log("msgXB", msgXB, "data", msgXB.Data.B)
}

type ShortMessage struct {
	Name string `json:"name"`
	Id   int    `json:"id"`
}

var msgAS = ShortMessage{
	Name: "msg_as",
	Id:   1,
}

var dataA = AData{
	A: "a_data",
}

var msgBS = ShortMessage{
	Name: "msg_bs",
	Id:   2,
}

var dataB = BData{
	B: "b_data",
}

func TestJsonStructSplit(t *testing.T) {
	// marshal

	msgAJ, _ := json.Marshal(struct {
		*ShortMessage
		*AData
	}{&msgAS, &dataA})

	msgBJ, _ := json.Marshal(struct {
		*ShortMessage
		*BData
	}{&msgBS, &dataB})

	// unmarshal

	var msgXA ShortMessage
	var dataXA AData
	_ = json.Unmarshal(msgAJ, &struct {
		*ShortMessage
		*AData
	}{&msgXA, &dataXA})
	t.Log("msgXA", msgXA, "data", dataXA.A)

	var msgXB ShortMessage
	var dataXB BData
	_ = json.Unmarshal(msgBJ, &struct {
		*ShortMessage
		*BData
	}{&msgXB, &dataXB})
	t.Log("msgXB", msgXB, "data", dataXB.B)
}

func TestJsonStructFull(t *testing.T) {
	// marshal

	msgAJ, _ := json.Marshal(msgA)
	msgBJ, _ := json.Marshal(msgB)

	// unmarshal

	var msgXA Message
	var dataXA AData
	_ = json.Unmarshal(msgAJ, &struct {
		*Message
		*AData `json:"data"`
	}{&msgXA, &dataXA})
	msgXA.Data = dataXA
	t.Log("msgXA", msgXA, "data", msgXA.Data.(AData).A)

	var msgXB Message
	var dataXB BData
	_ = json.Unmarshal(msgBJ, &struct {
		*Message
		*BData `json:"data"`
	}{&msgXB, &dataXB})
	msgXB.Data = dataXB
	t.Log("msgXB", msgXB, "data", msgXB.Data.(BData).B)
}

参考

Licensed under CC BY-NC-SA 4.0
最后更新于 2021-09-24 13:55 +0800
使用 Hugo 构建
主题 StackJimmy 设计