@@ -6,3 +6,309 @@ title: Go JSON tips
6
6
* TOC
7
7
{: toc }
8
8
9
+ There are times we are passed with a field of string type, but we wish it to be int.
10
+ If we know ` json:",string" ` , then it should be a easy.
11
+ Otherwise, it will take some serious time to do the conversion.
12
+
13
+ Rerference: http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/
14
+
15
+ # Ad-hoc ignore some field
16
+
17
+ ``` golang
18
+ type User struct {
19
+ Email string ` json:"email"`
20
+ Password string ` json:"password"`
21
+ // many more fields…
22
+ }
23
+ ```
24
+
25
+ to ignore ` Password ` field
26
+
27
+ ``` golang
28
+ json.Marshal (struct {
29
+ *User
30
+ Password bool ` json:"password,omitempty"`
31
+ }{
32
+ User : user,
33
+ })
34
+ ```
35
+
36
+ # Ad-hoc add extra field
37
+
38
+ ``` golang
39
+ type User struct {
40
+ Email string ` json:"email"`
41
+ Password string ` json:"password"`
42
+ // many more fields…
43
+ }
44
+ ```
45
+
46
+ Ignore the ` Password ` field and add new ` Token ` field
47
+
48
+ ``` golang
49
+ json.Marshal (struct {
50
+ *User
51
+ Token string ` json:"token"`
52
+ Password bool ` json:"password,omitempty"`
53
+ }{
54
+ User : user,
55
+ Token : token,
56
+ })
57
+ ```
58
+
59
+ # Ad-hoc combine two structs
60
+
61
+ ``` golang
62
+ type BlogPost struct {
63
+ URL string ` json:"url"`
64
+ Title string ` json:"title"`
65
+ }
66
+
67
+ type Analytics struct {
68
+ Visitors int ` json:"visitors"`
69
+ PageViews int ` json:"page_views"`
70
+ }
71
+
72
+ json.Marshal (struct {
73
+ *BlogPost
74
+ *Analytics
75
+ }{post, analytics})
76
+ ```
77
+
78
+ # Ad-hoc split one json into two
79
+
80
+ ``` golang
81
+ json.Unmarshal ([]byte (` {
82
+
83
+ "title": "Attila's Blog",
84
+ "visitors": 6,
85
+ "page_views": 14
86
+ }` ), &struct {
87
+ *BlogPost
88
+ *Analytics
89
+ }{&post, &analytics})
90
+ ```
91
+
92
+ # Ad-hoc rename field
93
+
94
+ ``` golang
95
+ type CacheItem struct {
96
+ Key string ` json:"key"`
97
+ MaxAge int ` json:"cacheAge"`
98
+ Value Value ` json:"cacheValue"`
99
+ }
100
+
101
+ json.Marshal (struct {
102
+ *CacheItem
103
+
104
+ // Omit bad keys
105
+ OmitMaxAge omit ` json:"cacheAge,omitempty"`
106
+ OmitValue omit ` json:"cacheValue,omitempty"`
107
+
108
+ // Add nice keys
109
+ MaxAge int ` json:"max_age"`
110
+ Value *Value ` json:"value"`
111
+ }{
112
+ CacheItem : item,
113
+
114
+ // Set the int by value:
115
+ MaxAge : item.MaxAge ,
116
+
117
+ // Set the nested struct by reference, avoid making a copy:
118
+ Value : &item.Value ,
119
+ })
120
+ ```
121
+
122
+ # Pass numbers as string
123
+
124
+ ``` golang
125
+ type TestObject struct {
126
+ Field1 int ` json:",string"`
127
+ }
128
+ ```
129
+
130
+ The corresponding json should be ` {"Field1": "100"} `
131
+
132
+ For ` {"Field1": 100} ` , there will be error
133
+
134
+ # String/number fuzzy conversion
135
+
136
+ If you are using jsoniter, enable fuzzy decoders can make life easier working with PHP.
137
+
138
+ ``` golang
139
+ import " github.com/json-iterator/go/extra"
140
+
141
+ extra.RegisterFuzzyDecoders ()
142
+ ```
143
+
144
+ Then, no matter the input is string or number, or the output is number or string, jsoniter will convert it for you. For example:
145
+
146
+ ``` golang
147
+ var val string
148
+ jsoniter.UnmarshalFromString (` 100` , &val)
149
+ ```
150
+
151
+ And
152
+
153
+ ``` golang
154
+ var val float32
155
+ jsoniter.UnmarshalFromString (` "1.23"` , &val)
156
+ ```
157
+
158
+ # [ ] as object
159
+
160
+ Another heart breaking "feature" of PHP is that, when array is empty, it is encoded as ` [] ` instead of ` {} ` .
161
+
162
+ If you are using jsoniter, enable fuzzy decoders will also fix this.
163
+
164
+ ``` golang
165
+ import " github.com/json-iterator/go/extra"
166
+
167
+ extra.RegisterFuzzyDecoders ()
168
+ ```
169
+
170
+ Then we can deal with ` [] `
171
+
172
+ ``` golang
173
+ var val map [string ]interface {}
174
+ jsoniter.UnmarshalFromString (` []` , &val)
175
+ ```
176
+
177
+ # Customize time.Time with MarshalJSON
178
+
179
+ The defeault implementation will encode time.Time as string. If we want to represent time in other format, we have to define a new type
180
+ for time.Time, with MarshalJSON.
181
+
182
+ ``` golang
183
+ type timeImplementedMarshaler time.Time
184
+
185
+ func (obj timeImplementedMarshaler ) MarshalJSON () ([]byte , error ) {
186
+ seconds := time.Time (obj).Unix ()
187
+ return []byte (strconv.FormatInt (seconds, 10 )), nil
188
+ }
189
+ ```
190
+
191
+ Marshal will invoke MarshalJSON on types defined them.
192
+
193
+ ``` golang
194
+ type TestObject struct {
195
+ Field timeImplementedMarshaler
196
+ }
197
+ should := require.New (t)
198
+ val := timeImplementedMarshaler (time.Unix (123 , 0 ))
199
+ obj := TestObject {val}
200
+ bytes , err := jsoniter.Marshal (obj)
201
+ should.Nil (err)
202
+ should.Equal (` {"Field":123}` , string (bytes))
203
+ ```
204
+
205
+ # Use RegisterTypeEncoder to customize time.Time
206
+
207
+ Unlike the standard library, jsoniter allow you to customize types defined by other guys. For example, we can use epoch int64 to encode a time.Time.
208
+
209
+ ``` golang
210
+ import " github.com/json-iterator/go/extra"
211
+
212
+ extra.RegisterTimeAsInt64Codec (time.Microsecond )
213
+ output , err := jsoniter.Marshal (time.Unix (1 , 1002 ))
214
+ should.Equal (" 1000001" , string (output))
215
+ ```
216
+
217
+ If you want to do it yourself, reference ` RegisterTimeAsInt64Codec ` for more information.
218
+
219
+ # Non string key map
220
+
221
+ Although JSON standard requires the key of map to be string, golang support more types as long as they defined MarshalText(). For example:
222
+
223
+ ``` golang
224
+ f , _ , _ := big.ParseFloat (" 1" , 10 , 64 , big.ToZero )
225
+ val := map [*big.Float ]string {f: " 2" }
226
+ str , err := MarshalToString (val)
227
+ should.Equal (` {"1":"2"}` , str)
228
+ ```
229
+
230
+ Given ` big.Float ` is a type with MarshalText()
231
+
232
+ # Use json.RawMessage
233
+
234
+ Some part of JSON might be lacking a uniform schema, we can keep the original JSON as it is.
235
+
236
+ ``` golang
237
+ type TestObject struct {
238
+ Field1 string
239
+ Field2 json.RawMessage
240
+ }
241
+ var data TestObject
242
+ json.Unmarshal ([]byte (` {"field1": "hello", "field2": [1,2,3]}` ), &data)
243
+ should.Equal (` [1,2,3]` , string (data.Field2 ))
244
+ ```
245
+
246
+ # Use json.Number
247
+
248
+ By default, number decoded into interface{} will be of type float64. If the input is large, the precision loss might be a problem.
249
+ So we can ` UseNumber() ` to represent number as ` json.Number ` .
250
+
251
+ ``` golang
252
+ decoder1 := json.NewDecoder (bytes.NewBufferString (` 123` ))
253
+ decoder1.UseNumber ()
254
+ var obj1 interface {}
255
+ decoder1.Decode (&obj1)
256
+ should.Equal (json.Number (" 123" ), obj1)
257
+ ```
258
+
259
+ jsoniter 支持标准库的这个用法。同时,扩展了行为使得 Unmarshal 也可以支持 UseNumber 了。
260
+
261
+ ``` golang
262
+ json := Config {UseNumber:true }.Froze ()
263
+ var obj interface {}
264
+ json.UnmarshalFromString (" 123" , &obj)
265
+ should.Equal (json.Number (" 123" ), obj)
266
+ ```
267
+
268
+ # 统一更改字段的命名风格
269
+
270
+ 经常 JSON 里的字段名 Go 里的字段名是不一样的。我们可以用 field tag 来修改。
271
+
272
+ ``` golang
273
+ output , err := jsoniter.Marshal (struct {
274
+ UserName string ` json:"user_name"`
275
+ FirstLanguage string ` json:"first_language"`
276
+ }{
277
+ UserName : " taowen" ,
278
+ FirstLanguage : " Chinese" ,
279
+ })
280
+ should.Equal (` {"user_name":"taowen","first_language":"Chinese"}` , string (output))
281
+ ```
282
+
283
+ 但是一个个字段来设置,太麻烦了。如果使用 jsoniter,我们可以统一设置命名风格。
284
+
285
+ ``` golang
286
+ import " github.com/json-iterator/go/extra"
287
+
288
+ extra.SetNamingStrategy (LowerCaseWithUnderscores)
289
+ output , err := jsoniter.Marshal (struct {
290
+ UserName string
291
+ FirstLanguage string
292
+ }{
293
+ UserName : " taowen" ,
294
+ FirstLanguage : " Chinese" ,
295
+ })
296
+ should.Nil (err)
297
+ should.Equal (` {"user_name":"taowen","first_language":"Chinese"}` , string (output))
298
+ ```
299
+
300
+ # 使用私有的字段
301
+
302
+ Go 的标准库只支持 public 的 field。jsoniter 额外支持了 private 的 field。需要使用 ` SupportPrivateFields() ` 来开启开关。
303
+
304
+ ``` golang
305
+ import " github.com/json-iterator/go/extra"
306
+
307
+ extra.SupportPrivateFields ()
308
+ type TestObject struct {
309
+ field1 string
310
+ }
311
+ obj := TestObject {}
312
+ jsoniter.UnmarshalFromString (` {"field1":"Hello"}` , &obj)
313
+ should.Equal (" Hello" , obj.field1 )
314
+ ```
0 commit comments