@@ -6,3 +6,309 @@ title: Go JSON tips
66* TOC
77{: toc }
88
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+ "url": "attila@attilaolah.eu",
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