qs #
Zero-dependencies package to encodes structs into url.Values.
Installation
go get github.com/sonh/qs
Usage
import (
"github.com/sonh/qs"
)
Package qs exports the NewEncoder() function to create an encoder. The encoder caches struct information to speed up the encoding process; using a single instance is highly recommended.
Use the WithTagAlias() function to register a custom tag alias (the default is qs).
encoder = qs.NewEncoder(
qs.WithTagAlias("myTag"),
)Encoder has Values() and Encode() functions to encode structs into url.Values.
Supported data types:
- all basic types (
bool,uint,string,float64,...) structslice,arraypointertime.Time- custom type
Example
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"
Bool format
Useint 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"
Time format
By default, the package encodes time.Time values in RFC3339 format.Include the "second" or "millis" option to indicate that the field should be encoded as seconds or milliseconds.
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/Array Format
Slice and Array default to encoding into multiple URL values with the same key 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 the 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"
Nested structs
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"
By default, it uses the brackets, add the dot option to
a nested struct field to scope its children with .:type Query struct {
User User qs:"user,dot"
}values, _ := encoder.Values(query)
fmt.Println(values.Encode()) //(unescaped) output: "user.from=1601623397728&user.verified=true"
The dot option applies only to the field it is declared on; nest it again on a
deeper struct field to keep using dots (otherwise that level falls back to brackets).Custom Type
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"
Limitation
- if elements in
slice/arrayarestructdata type, multi-level nesting are limited - no decoder yet
License
Distributed under MIT License, please see license file in code for more details.--- Tranlated By Open Ai Tx | Last indexed: 2026-07-29 ---