Tags: #go #serialization
package main
import (
"bytes"
"fmt"
"net/url"
"github.com/ajg/form"
"github.com/google/go-querystring/query" // NOTE: Only handles encoding, not decoding!
"github.com/gorilla/schema"
"github.com/pasztorpisti/qs"
"github.com/hetiansu5/urlquery"
)
type A struct {
Services []string `form:"services"`
}
type B struct {
Services []string `url:"services,brackets,omitempty"` // needed the 'brackets' bit to make it work
}
type C struct {
Services []string `schema:"services"`
}
type D struct {
Services []string `qs:"services"`
}
type E struct {
Services []string `query:"services"`
}
func main() {
fmt.Println("we want: services[]=A&services[]=B\n")
fmt.Println("ajg/form")
a := A{Services: []string{"A", "B"}}
buf := new(bytes.Buffer)
form.NewEncoder(buf).DelimitWith('|').Encode(a)
u, _ := url.QueryUnescape(buf.String())
fmt.Println("❌", u)
fmt.Println("google/go-querystring")
b := B{Services: []string{"A", "B"}}
v, _ := query.Values(b)
fmt.Println("✅", v.Encode())
u, _ = url.QueryUnescape(v.Encode())
fmt.Println("✅", u)
fmt.Println("gorilla/schema")
c := C{Services: []string{"A", "B"}}
encoder := schema.NewEncoder()
form := url.Values{}
encoder.Encode(c, form)
fmt.Println("❌", form.Encode())
fmt.Println("pasztorpisti/qs")
d := D{Services: []string{"A", "B"}}
s, _ := qs.Marshal(&d)
fmt.Println("❌", s)
fmt.Println("hetiansu5/urlquery")
e := E{Services: []string{"A", "B"}}
bytes, _ := urlquery.Marshal(e)
fmt.Println("✅", string(bytes))
u, _ = url.QueryUnescape(string(bytes))
fmt.Println("✅", u)
}
package main
import (
"bytes"
"fmt"
"net/url"
"github.com/ajg/form"
)
type Compatibool bool
func (b Compatibool) MarshalText() ([]byte, error) {
if b {
return []byte("1"), nil
}
return []byte("0"), nil
}
type T bool
type options struct {
Foo string `form:"foo,omitempty"`
Int int `form:"integer,omitempty"`
TeeP *T `form:"tp,omitempty"`
Tee T `form:"t,omitempty"`
Services []string `form:"services,omitempty"`
CP *Compatibool `form:"cp,omitempty"`
C Compatibool `form:"c,omitempty"`
}
func TBool(b bool) *T {
t := T(b)
return &t
}
func CBool(b bool) *Compatibool {
c := Compatibool(b)
return &c
}
func main() {
var i interface{}
i = options{Foo: "b", TeeP: TBool(true), Tee: T(true), Services: []string{"A", "B"}, CP: CBool(true), C: Compatibool(true)}
buf := new(bytes.Buffer)
form.NewEncoder(buf).KeepZeros(true).DelimitWith('|').Encode(i)
u, _ := url.QueryUnescape(buf.String())
fmt.Println(u)
i = options{Foo: "b", TeeP: TBool(false), Tee: T(false), Services: []string{"A", "B"}, CP: CBool(false), C: Compatibool(false)}
buf = new(bytes.Buffer)
form.NewEncoder(buf).KeepZeros(true).DelimitWith('|').Encode(i)
u, _ = url.QueryUnescape(buf.String())
fmt.Println(u) // C is omitted as if it wasn't set.
}
package main
import (
"fmt"
"net/url"
"github.com/google/go-querystring/query"
)
type T bool
func (t T) EncodeValues(key string, v *url.Values) error {
fmt.Printf("key: %+v, t: %+v, v: %+v\n", key, t, v)
switch t {
case true:
v.Add(key, "1")
case false:
v.Add(key, "0")
}
return nil
}
type options struct {
Foo string `url:"foo,omitempty"`
TeeP *T `url:"tp,omitempty"`
Tee T `url:"t,omitempty"`
}
func main() {
fmt.Println("No TeeP or Tee set")
v, _ := query.Values(options{Foo: "a"})
fmt.Printf("v.Encode: %+v\n\n", v.Encode())
fmt.Println("TeeP and Tee set to true")
t := T(true)
v, _ = query.Values(options{Foo: "b", TeeP: &t, Tee: T(true)})
fmt.Printf("v.Encode: %+v\n\n", v.Encode())
fmt.Println("TeeP and Tee set to false")
t = T(false)
v, _ = query.Values(options{Foo: "c", TeeP: &t, Tee: T(false)})
fmt.Printf("v.Encode: %+v\n\n", v.Encode()) // NOTE: Tee doesn't cause EncodeValues() to be called?
}
/*
No TeeP or Tee set
v.Encode: foo=a
TeeP and Tee set to true
key: tp, t: true, v: &map[foo:[b]]
key: t, t: true, v: &map[foo:[b] tp:[1]]
v.Encode: foo=b&t=1&tp=1
TeeP and Tee set to false
key: tp, t: false, v: &map[foo:[c]]
v.Encode: foo=c&tp=0
*/
package main
import (
"fmt"
"net/url"
"github.com/hetiansu5/urlquery"
)
// An OptionData is test structure
type OptionData struct {
Services []string `query:"services,omitempty"`
TeeP *T `query:"tp,omitempty"`
Tee T `query:"t,omitempty"`
Int int `query:"integer,omitempty"`
}
// A SelfQueryEncoder is test structure
type SelfQueryEncoder struct{}
// Escape is test func
func (u SelfQueryEncoder) Escape(s string) string {
fmt.Println("Escape", s)
return url.QueryEscape(s)
}
// UnEscape is test func
func (u SelfQueryEncoder) UnEscape(s string) (string, error) {
fmt.Println("UnEscape", s)
return url.QueryUnescape(s)
}
type T bool
func marshal(data OptionData) {
fmt.Printf("%+v\n", data)
builder := urlquery.NewEncoder(
urlquery.WithNeedEmptyValue(true),
urlquery.WithQueryEncoder(SelfQueryEncoder{}))
bytes, err := builder.Marshal(data)
if err != nil {
fmt.Println(err)
return
}
u, _ := url.QueryUnescape(string(bytes))
fmt.Println(u)
}
func main() {
marshal(OptionData{
Services: []string{
"A",
"B",
},
})
marshal(OptionData{
Services: []string{
"A",
"B",
},
Int: 123,
})
marshal(OptionData{
Services: []string{
"A",
"B",
},
Int: 0, // is omitted if we set WithNeedEmptyValue(true) but we don't WANT that option set because it means even if the field is not set by the user, it will have a default value set in the output
})
tp := T(true)
marshal(OptionData{
Services: []string{
"A",
"B",
},
TeeP: &tp,
Tee: T(true),
})
tp = T(false)
marshal(OptionData{
Services: []string{
"A",
"B",
},
TeeP: &tp,
Tee: T(false),
})
}