Zero-dependencies package to encodes structs into url.Values.
go get github.com/sonh/qsimport (
"github.com/sonh/qs"
)Package qs exports NewEncoder() function to create an encoder.
Encoder caches struct info to speed up encoding process, use a single instance is highly recommended.
Use WithTagAlias() func to register custom tag alias (default is qs)
encoder = qs.NewEncoder(
qs.WithTagAlias("myTag"),
)Encoder has Values() and Encode() functions to encode structs into url.Values.
- all basic types (
bool,uint,string,float64,...) structslice,arraypointertime.Time- custom type
type Query struct {
Tags []string `qs:"tags"`
Limit int `qs:"limit"`
From time.Time `qs:"from"`
Active bool `qs:"active,omitempty"` //omit empty value
Ignore float64 `qs:"-"` //ignore
}
query := &Query{
Tags: []string{"docker", "golang", "reactjs"},
Limit: 24,
From: time.Unix(1580601600, 0).UTC(),
Ignore: 0,
}
encoder := qs.NewEncoder()
values, err := encoder.Values(query)
if err != nil {
// Handle error
}
fmt.Println(values.Encode()) //(unescaped) output: "from=2020-02-02T00:00:00Z&limit=24&tags=docker&tags=golang&tags=reactjs"Use int option to encode bool to integer
type Query struct {
DefaultFmt bool `qs:"default_fmt"`
IntFmt bool `qs:"int_fmt,int"`
}
query := &Query{
DefaultFmt: true,
IntFmt: true,
}
values, _ := encoder.Values(query)
fmt.Println(values.Encode()) // (unescaped) output: "default_fmt=true&int_fmt=1"By default, package encodes time.Time values as RFC3339 format.
Including the "second" or "millis" option to signal that the field should be encoded as second or millisecond.
type Query struct {
Default time.Time `qs:"default_fmt"`
Second time.Time `qs:"second_fmt,second"` //use `second` option
Millis time.Time `qs:"millis_fmt,millis"` //use `millis` option
}
t := time.Unix(1580601600, 0).UTC()
query := &Query{
Default: t,
Second: t,
Millis: t,
}
encoder := qs.NewEncoder()
values, _ := encoder.Values(query)
fmt.Println(values.Encode()) // (unescaped) output: "default_fmt=2020-02-02T00:00:00Z&millis_fmt=1580601600000&second_fmt=1580601600"Slice and Array default to encoding into multiple URL values of the same value name.
type Query struct {
Tags []string `qs:"tags"`
}
values, _ := encoder.Values(&Query{Tags: []string{"foo","bar"}})
fmt.Println(values.Encode()) //(unescaped) output: "tags=foo&tags=bar"Including the comma option to signal that the field should be encoded as a single comma-delimited value.
type Query struct {
Tags []string `qs:"tags,comma"`
}
values, _ := encoder.Values(&Query{Tags: []string{"foo","bar"}})
fmt.Println(values.Encode()) //(unescaped) output: "tags=foo,bar"Including the bracket option to signal that the multiple URL values should have "[]" appended to the value name.
type Query struct {
Tags []string `qs:"tags,bracket"`
}
values, _ := encoder.Values(&Query{Tags: []string{"foo","bar"}})
fmt.Println(values.Encode()) //(unescaped) output: "tags[]=foo&tags[]=bar"The index option will append an index number with brackets to value name.
type Query struct {
Tags []string `qs:"tags,index"`
}
values, _ := encoder.Values(&Query{Tags: []string{"foo","bar"}})
fmt.Println(values.Encode()) //(unescaped) output: "tags[0]=foo&tags[1]=bar"All nested structs are encoded including the parent value name with brackets for scoping.
type User struct {
Verified bool `qs:"verified"`
From time.Time `qs:"from,millis"`
}
type Query struct {
User User `qs:"user"`
}
query := Query{
User: User{
Verified: true,
From: time.Now(),
},
}
values, _ := encoder.Values(query)
fmt.Println(values.Encode()) //(unescaped) output: "user[from]=1601623397728&user[verified]=true"Implement funcs:
EncodeParamto encode itself into query param.IsZeroto check whether an object is zero to determine whether it should be omitted when encoding.
type NullableName struct {
First string
Last string
}
func (n NullableName) EncodeParam() (string, error) {
return n.First + n.Last, nil
}
func (n NullableName) IsZero() bool {
return n.First == "" && n.Last == ""
}
type Struct struct {
User NullableName `qs:"user"`
Admin NullableName `qs:"admin,omitempty"`
}
s := Struct{
User: NullableName{
First: "son",
Last: "huynh",
},
}
encoder := qs.NewEncoder()
values, err := encoder.Values(&s)
if err != nil {
// Handle error
fmt.Println("failed")
return
}
fmt.Println(values.Encode()) //(unescaped) output: "user=sonhuynh"- if elements in
slice/arrayarestructdata type, multi-level nesting are limited - no decoder yet
Will improve in future versions
Distributed under MIT License, please see license file in code for more details.