qs

package module
v0.6.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 8, 2024 License: MIT Imports: 7 Imported by: 7

README

qs

Build Codecov GoReportCard Release PkgGoDev MIT License

Package sonh/qs encodes structs into url.Values.

Installation

go get github.com/sonh/qs

Usage

import (
    "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.

Supported data types:
  • all basic types (bool, uint, string, float64,...)
  • struct
  • slice, array
  • pointer
  • time.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

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"
Time format

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/Array Format

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"
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"
Custom Type

Implement funcs:

  • EncodeParam to encode itself into query param.
  • IsZero to 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/array are struct data type, multi-level nesting are limited
  • no decoder yet

Will improve in future versions

License

Distributed under MIT License, please see license file in code for more details.

Documentation

Overview

Package qs encodes structs into url.Values.

Package exports `NewEncoder()` function to create an encoder. 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.

Supported data types:

  • all basic types (`bool`, `uint`, `string`, `float64`,...)
  • struct
  • slice/array
  • pointer
  • time.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"`
	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"

Ignoring Fields

type Struct struct {
	Field string `form:"-"` //using `-` to to tell qs to ignore fields
}

Omitempty

type Struct struct {
	Field1 string	`form:",omitempty"`			//using `omitempty` to to tell qs to omit empty field
	Field2 *int		`form:"field2,omitempty"`
}

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,
	Decimal: decimal.NewFromFloat(0.012147483648),
}

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"`
}

querys := Query{
	User: User{
		Verified: true,
		From: time.Now(),
	},
}
values, _ := encoder.Values(querys)
fmt.Println(values.Encode()) //(unescaped) output: "user[from]=1601623397728&user[verified]=true"

Custom type Implement `EncodeParam` to encode itself into query param. Implement `IsZero` to 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

  • `interface`, `[]interface`, `map` are not supported yet
  • `struct`, `slice`/`array` multi-level nesting are limited
  • no decoder yet

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder is the main instance Apply options by using WithTagAlias, WithCustomType

func NewEncoder

func NewEncoder(options ...EncoderOption) *Encoder

NewEncoder init new *Encoder instance Use EncoderOption to apply options

func (*Encoder) Encode

func (e *Encoder) Encode(v interface{}, values url.Values) error

Encode encodes a struct into the given url.Values v must be struct data type

func (*Encoder) Values

func (e *Encoder) Values(v interface{}) (url.Values, error)

Values encodes a struct into url.Values v must be struct data type

type EncoderOption

type EncoderOption func(encoder *Encoder)

EncoderOption provides option for Encoder

func WithTagAlias

func WithTagAlias(tagAlias string) EncoderOption

WithTagAlias create a option to set custom tag alias instead of `qs`

type QueryParamEncoder added in v0.6.0

type QueryParamEncoder interface {
	EncodeParam() (string, error)
}

QueryParamEncoder is an interface implemented by any type to encode itself into query param

type Zeroer added in v0.6.0

type Zeroer interface {
	IsZero() bool
}

Zeroer represents an object has zero value IsZeroer is used to check whether an object is zero to determine whether it should be omitted when encoding

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL