有的时候上游传过来的字段是string类型的,但是我们却想用变成数字来使用。 本来用一个json:",string" 就可以支持了,如果不知道golang的这些小技巧,就要大费周章了。
参考文章:http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/
1. 临时忽略struct空字段
type User struct {
Email string `json:"email"`
Password string `json:"password"`
// many more fields…
}
如果想临时忽略掉空Password字段,可以用omitempty:
json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: user,
})
2. 解析成string类型
type User struct {
Id uint64 `json:"id,string"`
}
func main() {
user := User{Id: 123}
bs, _ := json.Marshal(user)
fmt.Println(string(bs))
}
输出
{“id”:“123”}
3. 临时添加额外的字段
type User struct {
Email string `json:"email"`
Password string `json:"password"`
// many more fields…
}
临时忽略掉空Password字段,并且添加token字段
json.Marshal(struct {
*User
Token string `json:"token"`
Password bool `json:"password,omitempty"`
}{
User: user,
Token: token,
})
4. 临时粘合两个struct
通过嵌入struct的方式:
type BlogPost struct {
URL string `json:"url"`
Title string `json:"title"`
}
type Analytics struct {
Visitors int `json:"visitors"`
PageViews int `json:"page_views"`
}
json.Marshal(struct{
*BlogPost
*Analytics
}{post, analytics})
5. 一个json切分成两个struct
我们可以通过一个大的struct,将一个json串解析到两个struct里面
json.Unmarshal([]byte(`{
"url": "attila@attilaolah.eu",
"title": "Attila's Blog",
"visitors": 6,
"page_views": 14
}`), &struct {
*BlogPost
*Analytics
}{&post, &analytics})
6. 用字符串传递数字
type TestObject struct {
Field1 int `json:",string"`
}
这个对应的json是 {“Field1”: “100”}
如果json是 {“Field1”: 100} 则会报错
容忍字符串和数字互转
如果你使用的是jsoniter,可以启动模糊模式来支持 PHP 传递过来的 JSON。
import "github.com/json-iterator/go/extra"
extra.RegisterFuzzyDecoders()
这样就可以处理字符串和数字类型不对的问题了。比如
import (
"fmt"
jsoniter "github.com/json-iterator/go"
"github.com/json-iterator/go/extra"
)
type User struct {
Id int `json:"id"`
Name string `json:"name"`
}
func main() {
// 开启模糊模式
extra.RegisterFuzzyDecoders()
s := `{"id": "123", "name": 456}`
var user User
_ = jsoniter.UnmarshalFromString(s, &user)
fmt.Println(user)
}
7. 容忍空数组作为对象
PHP另外一个令人崩溃的地方是,如果 PHP array是空的时候,序列化出来是[]。但是不为空的时候,序列化出来的是{“key”:“value”}。 我们需要把 [] 当成 {} 处理。
如果你使用的是jsoniter,可以启动模糊模式来支持 PHP 传递过来的 JSON。
import "github.com/json-iterator/go/extra"
// 开启模糊模式
extra.RegisterFuzzyDecoders()
var val map[string]interface{}
jsoniter.UnmarshalFromString(`[]`, &val)
8. 记录原始信息
如果部分json文档没有标准格式,我们可以使用 json.RawMessage把原始的信息用[]byte保存下来。
type TestObject struct {
Field1 string
Field2 json.RawMessage
}
var data TestObject
json.Unmarshal([]byte(`{"field1": "hello", "field2": [1,2,3]}`), &data)
should.Equal(` [1,2,3]`, string(data.Field2))
9. 使用 json.Number
默认情况下,如果是 interface{} 对应数字的情况会是 float64 类型的。如果输入的数字比较大,将会损精度。所以可以 UseNumber() 启用 json.Number 来用字符串表示数字。
var tt interface{}
decoder := json.NewDecoder(bytes.NewBufferString(`1234567`))
decoder.UseNumber() // 默认序列化为int类型
decoder.Decode(&tt)
fmt.Println(tt)
jsoniter 支持标准库的这个用法。同时,扩展了行为使得 Unmarshal 也可以支持 UseNumber 了。
json := jsoniter.Config{UseNumber: true}.Froze()
json.Unmarshal([]byte("1234567"), &tt)
fmt.Println(tt)
10. 统一更改字段的命名风格
经常 JSON 里的字段名 Go 里的字段名是不一样的。我们可以用 field tag 来修改。
output, err := jsoniter.Marshal(struct {
UserName string `json:"user_name"`
FirstLanguage string `json:"first_language"`
}{
UserName: "taowen",
FirstLanguage: "Chinese",
})
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))
但是一个个字段来设置,太麻烦了。如果使用 jsoniter,我们可以统一设置命名风格。
import "github.com/json-iterator/go/extra"
extra.SetNamingStrategy(LowerCaseWithUnderscores)
output, err := jsoniter.Marshal(struct {
UserName string
FirstLanguage string
}{
UserName: "taowen",
FirstLanguage: "Chinese",
})
should.Nil(err)
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))
11. 自定义解析时间
golang 默认会把 time.Time 用字符串方式序列化。如果我们想用其他方式表示 time.Time,需要自定义类型并定义 MarshalJSON。
const TimeFormat = "2006-01-02 15:04:05"
type LocalTime time.Time
func (t *LocalTime) UnmarshalJSON(data []byte) (err error) {
if len(data) == 2 {
*t = LocalTime(time.Time{})
return
}
now, err := time.Parse(`"`+TimeFormat+`"`, string(data))
*t = LocalTime(now)
return
}
func (t LocalTime) MarshalJSON() ([]byte, error) {
b := make([]byte, 0, len(TimeFormat)+2)
b = append(b, '"')
b = time.Time(t).AppendFormat(b, TimeFormat)
b = append(b, '"')
return b, nil
}
func (t LocalTime) String() string {
return time.Time(t).Format(TimeFormat)
}
使用
type User struct {
AddTime LocalTime `json:"add_time"`
}
func main() {
s := `{"add_time": "2020-07-31 22:00:00"}`
var user User
json.Unmarshal([]byte(s), &user)
fmt.Println(user)
}