Skip to content

Commit b700ae6

Browse files
authored
Update go-tips.md
1 parent f953e40 commit b700ae6

File tree

1 file changed

+306
-0
lines changed

1 file changed

+306
-0
lines changed

go-tips.md

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
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

Comments
 (0)