-
Notifications
You must be signed in to change notification settings - Fork 0
/
第 12.09.01 章、encoding_json 包
450 lines (275 loc) · 14.3 KB
/
第 12.09.01 章、encoding_json 包
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
json 包实现了编码和解码 JSON 文件的方法
JSON 类型与 Go 类型的对应关系在 Marshal 和 Unmarshal 函数中说明
Compact(dst *bytes.Buffer, src []byte) error
将 JSON 编码过的字节流,去掉无用的空白字符,并追加至目标 Buffer 的尾部
HTMLEscape(des *bytes.Buffer, src []byte)
将 JSON 中出现的 HTML 特殊字符都转义为非特殊字符
Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
向目标 Buffer 尾部追加 缩进过的 JSON 数据
每个 JSON 元素都放在新的一行中,且这行的行首追加 prefix 定义的字符串,缩进将使用 indent 定义的字符串
原 JSON 中行首的 空白字符都会被省略,但行尾的都会被保留
Marshal(v interface{}) ([]byte, error)
Marshal 函数返回 JSON 编码的 数据 v,(Marshal 在英语中有编队的含义)
Marshal 会递归扫描 v 的值。如果遇到的值实现了 Marshaler 接口,并且不为 nil 指针,则 Marshal 调用该值的 MarshalJSON 方法来产生 JSON 数据
若没有实现 MarshalJSON 方法,但实现了 encoding.TextMarshaler,则 Marshal 调用 MarshalText 方法来产生 JSON 字符串
nil 指针 exception 并非严格需要,而是对 UnmarshalJSON 行为的一种模仿
其他情况下, Marshal 遵循下列 类型相关 默认编码:
布尔值 编码为 JSON 布尔值
浮点数、整数、 Number 值 编码为 JSON 数字
字符串 编码为 JSON 字符串,并强制转换为有效 UTF-8 字符,将无效字符传唤为 Unicode replacement rune
尖角括号 < 和 > 转义为 \u003c 和 \u003e,防止部分浏览器将 JSON 数据误认为 HTML 数据
& 符号 转义为 \u0026,同样为了防止浏览器误认
可以调用 Encoder 的 SetEscapeHTML(false) 方法来关闭该 Encoder 实例的转义功能
array 和 slice 编码为 JSON array
特例:
[]byte 转化为 base64 编码的字符串
nil slice 编码为 null JSON 值
结构体 编码为 JSON 对象
每个 导出的 结构体字段成为 JSON 对象的一个成员,字段名成为对象的键名
特例,在下列情况下,字段会被省略或被修改:
在结构体字段的标签(Tag)中,若含有 "json" 关键字,则 JSON 的键名可以被标签自定义
标签中,`json:` 后跟随的内容将作为键名,其后可能会跟随一些 逗号分隔 的选项列表
`json:` 后的名称可以被省略,而保留原始的键名,同时应用其后的选项列表
"omitempty" 选项表示,若字段为一个空值,则不将它编码,空值定义为: false、0、nil 指针、nil 接口值、空 array、空 slice、空 string
特别的,若字段标签仅为 "-",则字段一直被忽略,若想输出键名为 "-" 的字段,可以使用 "-,"
注:使用反引号是因为标签中常出现单/双引号
EG.
// 该字段对应 JSON 的键名为 "myName"
Field int `json:"myName"`
// 该字段对应 JSON 的键名为 "myName"
// 若值为 JSON 的空值,则不导出这个字段
Field int `json:"myName,omitempty"`
// 该字段对应的 JSON 的键名即为字段名
// 若值为 JSON 的空值,则不导出这个字段
// 注意 omitempty 前序的逗号
Field int `json:",omitempty"`
// 不导出该值至 JSON
Field int `json:"-"`
// 该值的 JSON 键名为 "-"
Field int "-,"
"string" 选项表示该值在 JSON 中以 JSON 编码的字符串的形式保存
该选线仅能应用在 Go 的字符串类型、浮点类型、整数类型、布尔类型
常用于与 JS 程序互动的情况
EG.
Int64String int64 `json:",string"`
若字段名为仅含 Unicode 字符、数字、ASCII 标点符号(不含引号、反斜线、逗号)的非空字符
则可用于 JSON 键名的编写
匿名结构字段的 JSON 编码,一般按照内部可导出字段完全置于外部结构的做法,相对于 Go 的字段可见性的修正见下文
匿名字段使用 标有 json 的 tag 作为其 JSON 名称,而非在 JSON 中匿名
若匿名字段的类型为 interface,则用类型名作为该 interface 的 JSON 键名
Go 的结构体可见性规则,在执行 JSON 的 marshal 和 unmarshal 将会得到修正,以配合 JSON 的编码标准
若多重字段存在于同一级别,且该级别已经是最简嵌套,则增加以下规则:
<1> 对于重复字段,仅有被标有 json 的 tag 的字段会加入 编码考虑,忽略其他重复字段
<2> 若仅有一个字段被 JSON 标记,则选择该字段
<3> 除上述两条之外,若还有多个字段,则全部忽略,不产生错误
实测结果:
<1> 将结构体所有的字段名通过 “有 tag 读取 tag 内容,无 tag 读取字段名” 的方法都转化为键名
<2> 若顶层的键名和非顶层键名重名,则仅保留顶层键名
<3> 若均为同级的插入结构体的键名重复,则全部丢弃,仅保留键名不重复的部分
Map 被编码为 JSON 对象
map 的键必须是 字符串、整型或者实现了 encoding.TextMarshaler 的类型
map 的键将被排序,并按照下述规则编码为 JSON 对象
<1> 字符串可以直接使用
<2> 实现了 encoding.TextMarshaler 的全部编码
<3> 整型值转换为字符串
指针编码为指针所指向的值
空指针编码为 JSON 的 null 对象
Interface 编码为 interface 所包含的对象
空 interface 编码为 JSON 的 null 对象
Channel/复数/函数 不可以被编码为 JSON 对象
编码上述类型的值会返回 UnsupportedTypeError
由于 JSON 不可以表示循环数据结构,所以 Marshal 不处理它们
对 Marshal 传入 循环结构将导致无限递归
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
输出 JSON 对象的时候,自动缩进
Unmarshal(data []byte, v interface{}) error
解码 JSON 数据,并将结果存入 v 中
v 是指向某个类型的变量的指针
Unmarshal 反向使用 Marshal 的方法,若需要则使用 map/slice/pointer,符合以下规则:
将 JSON 解码为 指针, Unmarshal 首先假设该 JSON 对象为 null 指针,获得一个 指向 nil 的指针
若 JSON 值不为空,则将 指针 指向这个值
将 JSON 对象解码为一个实现了 Unmarshaler 接口的类型
Unmarshal 调用该对象的 UnmarshalJSON 方法(JSON null 对象也是如此解码的)
其他情况,若类型实现了 encoding.TextUnmarshaler 接口,且输入的 JSON 是一个引号引起来的字符串
则调用该值得 UnmarshalText 方法,并解码为一个字符串
将 JSON 解码为一个结构体, Unmarshal 匹配 JSON 键名和 结构体的字段名(或 json tag 名)
优先匹配完全匹配的名称,但也可以匹配上忽略大小写的名称
仅有被导出的字段会被匹配
要将 JSON 解码为一个 Interface 值, Unmarshal 将做以下对应:
bool,对应 JSON 布尔值
float64,对应 JSON 数字
string,对应 JSON 字符串
[]interface{},对应 JSON array
map[string]interface{},对应 JSON 对象
nil,对应 JSON null
将 JSON array 解码为 slice, Unmarshal 重置 slice 的长度至 0,
并逐次将元素追加在 slice 的后面
特例是,若 JSON array 为空,则 Unmarshal 将原 slice 替换为一个空的 slice
将 JSON array 解码为 Go array, Unmarshal 将 JSON 元素解码为对应的 Go array 元素
若 Go array 的长度小于 JSON array 的长度,则多余的 JSON array 元素被抛弃
若 Go array 的长度大于 JSON array 的长度,则多余的 Go array 元素被填上 零值
将一个 JSON 物体解码为 map, Unmarshal 首先新建一个 map
若 map 为 nil,则 Unmarshal 会重定位一个新的 map
除此之外, Unmarshal 会重复利用已存在的 map,并保留当前的已存在的条目
接着从 JSON 对象中解码 键-值对,并输入 map 中
map 的键必须是 string/integer 或者 实现了 encoding.TextUnmarshaler 的类型
若 JSON 值无法匹配目标类型,或者 JSON 数字使目标类型溢出
Unmarshal 会跳过该位置,并试图继续解码剩下的部分
若其后没有非常严重的错误发生,则 Unmarshal 会在解码后返回一个 UnmarshalTypeError 错误
解码 JSON null 会将 interface/map/pointer/slice 的值变为 nil
解码 JSON null 值其他的类型的值,将不会对它们的产生影响并且不会产生错误
若解码引号引起来的字符串,出现了无效的 UTF-8 字符或者 无效的 UTF-16 代理对
并不会触发错误,而非法字符将会被 Unicode 字符 U+FFFD 替代
Decoder type 类型
从输入流中读取并解码 JSON 值
NewDecoder(r io.Reader) *Decoder
返回一个从 r 读取数据的解码器
解码器会自己引入 buffer,并可能读取超过解码 JSON 值所需的数据量
(dec *Decoder) Bufferd() io.Reader
返回在 Decoder 的 buffer 中剩下的数据的一个 reader
直到下次调用 Decode 前,这个 reader 都是有效的
(dec *Decoder) Decode(v interface{}) error
读取下一个 JSON 编码的元素,并将解码后的数据存入指向 v 的指针
具体细节参看 Unmarshal 函数
(dec *Decoder) More() bool
反馈当前 JSON array 或 JSON 对象 中否是还有下一个可以解析的元素
(dec *Decoder) Token() (Token, error)
返回输入流中下一个 JSON token
若到达输入流的结尾,Token 返回值为 nil, io.EOF
Token 保证了定界符 [ ] 和 { } 正确的嵌套匹配
若 Token 遇上了未知的定界符,则返回一个错误
输入流由基础的 JSON 值——布尔值、字符串、数字、null——和类型为 Delim 的定界符 [] {} 构成
定界符用于标记 JSON array 和 JSON 对象的开始和结束
逗号了冒号将被忽略
(dec *Decoder) UseNumber()
让 Decoder 将一个 JSON 数字解析为 Number 类型,而非 float64 类型
Delim rune 类型
该类型包含 JSON 的 array 或者 对象 的定界符 [ ] { }
(d Delim) String() string
字符串打印 Delim 类型的值时使用
Encoder struct 类型
将 JSON 值写入输出流
NewEncoder(w io.Writer) *Encoder
返回一个写入 w 的 编码器
(enc *Encoder) Encode(v interface{}) error
向输出流中写入 JSON 编码后的 变量 v,并跟随一个换行字符
详细规则参见 Marshal 函数
(*Encoder) SetEscapeHTML(on bool)
设置是否启动为 HTML 准备的字符转义
(*Encoder) SetIndent(prefix, indent string)
指示 encoder 格式化输出其下每个编码过的值
让它们看起来和调用了包级别函数 Indent 一样
调用 SetIndent("","") 方法清除这个设置
InvalidUTF8Error struct 类型
与 Go1.2 之前的版本保持兼容
由于 Go1.2 之后,不可编码的 UTF 码点都会被 U+FFFD 替代
该错误将不会被触发
type InvalidUTF8Error struct{
S string // 产生错误的整个字符串
}
(e *InvalidUTF8Error) Error() string
实现了该类型的报错接口
InvalidUnmarshalError struct 类型
传入了无效的待解码参数(传入参数必须是非空指针)
type InvalidUnmarshalError struct{
Type reflect.Type
}
(e *InvalidUnmarshalError) Error() string
实现了该类型的报错接口
Marshaler interface 类型
所有可以让自身类型转换为有效的 JSON 数据的类型都实现了 Marshaler 接口
type Marshaler interface{
MarshalJSON() ([]byte, error)
}
MarshalerError struct 类型
type MarshalerError struct{
Type reflect.Type
Err error
}
(e *MarshalerError) Error() string
Number string 类型
表示了一个 JSON 数字的字面值
(n Number) Float64() (float64, error)
数字作为 float64 值返回
(n Number) Int64() (int64, error)
数字作为 int64 值返回
(n Number) String() string
数字作为 字符串 值返回
RawMessage []byte 类型
该类型的值是一个已经被编码出来的 JSON 值
它实现了 Marshaler 和 Unmarshaler 用于其后的 JSON 解码或者 JSON 预编码
【也就是说,任何一个 []byte 类型 都可以是 RawMessage,也就时 JSON 最后呈现的样子】
EG.
h := json.RawMessage(`{"precomputed": true}`)
c := struct {
Header *json.RawMessage `json:"header"`
Body string `json:"body"`
}{Header: &h, Body: "Hello Gophers!"}
b, err := json.MarshalIndent(&c, "", "\t")
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
(m RawMessage) MarshalJSON() ([]byte, error)
将编码后的字节流重新加入其它的 JSON 编码过程
(m *RawMessage) UnmarshalJSON(data []byte) error
从字节流中读取数据
SyntaxError struct 类型
描述了 JSON 语法错误
type SyntaxError struct{
Offset int64 // 在发生错误时,已经读取的字节数
// 含有未导出的字段
}
(e *SyntaxError) Error() string
实现了该类型的报错接口
Token interface{} 类型
Token 可以具有一个值,该值为以下几种类型中的一种
Delim, JSON 定界符
bool, JSON 布尔值
float64, JSON 数字
Number, JSON 数字
string, JSON 字符
nil, JSON null
UnmarshalFieldError struct 类型
【不再使用,向前兼容】
描述了一个指向结构体中未导出字段(也就时不可写入)的 JSON 对象的键
type UnmarshalFieldError struct {
Key string
Type reflect.Type
Field reflect.StructField
}
func (e *UnmarshalFieldError) Error() string
实现了该类型的报错接口
UnmarshalTypeError struct 类型
描述了一个 JSON 值不能与指定的 Go 类型匹配
type UnmarshalTypeError struct {
Value string // 描述了 JSON 值
Type reflect.Type // 不可以被赋予的 Go 类型
Offset int64 // 已经读取的字节偏移量
Struct string // 包含该字段的结构体名称
Field string // 包含该字段的 Go 的值
}
(e *UnmarshalTypeError) Error() string
实现了该类型的报错接口
Unmarshaler interface 类型
所有可以让 JSON 数据转换为有效的自身类型都实现了 Unmarshaler 接口
输入被假设为有效编码的 JSON 值
若要在方法返回后保存原始数据,UnmarshalJSON 必须拷贝 JSON 数据
为了方便贴近 Unmarshal 函数的行为, Unmarshaler 将 UnmarshalJSON([]byte("null")) 作为 不操作 的实现
type Unmarshaler interface{
UnmashalJSON([]byte) error
}
UnsupportedTypeError struct 类型
当尝试编码不收支持的值类型时, Marshal 返回该错误
type UnsupportedTypeError struct {
Type reflect.Type
}
(e *UnsupportedTypeError) Error() string
实现了该类型的报错接口
UnsupportedValueError struct 类型
type UnsupportedValueError struct {
Value reflect.Value
Str string
}
(e *UnsupportedValueError) Error() string
实现了该类型的报错接口