GitHub - monoculum/formam: a package for decode form's values into struct in Go (original) (raw)

formam

A Go package to decode HTTP form and query parameters. The only requirement is Go 1.12 or later.

Build Status GoDoc

Install

go get github.com/monoculum/formam/v3

Features

Performance

You can see the performance in formam-benchmark compared with ajg/form, gorilla/schema, go-playground/form and built-in/json.

Basic usage example

In form HTML

In Go

You can use the formam struct tag to ensure the form values are unmarshalled in the currect struct fields.

type InterfaceStruct struct { ID int Name string }

type Company struct { Public bool formam:"public" Website url.URL formam:"website" Foundation time.Time formam:"foundation" Name string Location struct { Country string City string } Products []struct { Name string Type string } Founders []string Employees int64

Interface interface{} }

func MyHandler(w http.ResponseWriter, r *http.Request) error { r.ParseForm()

m := Company{ // it's is possible to access to the fields although it's an interface field! Interface: &InterfaceStruct{}, } dec := formam.NewDecoder(&formam.DecoderOptions{TagName: "formam"}) return dec.Decode(r.Form, &m) }

Types

Supported types in the destination struct are:

Custom Marshaling

You can umarshal data and map keys by implementing the encoding.TextUnmarshaler interface.

If the forms sends multiple values then only the first value is passed to UnmarshalText(), but if the name ends with [] then it's called for all values.

Custom Type

You can register a function for a custom type using the RegisterCustomType() method. This will work for any number of given fields or all fields with the given type.

Registered type have preference over the UnmarshalText method unless the PrefUnmarshalText option is used.

All fields

decoder.RegisterCustomType(func(vals []string) (interface{}, error) { return time.Parse("2006-01-02", vals[0]) }, []interface{}{time.Time{}}, nil)

Specific fields

package main

type Times struct { Timestamp time.Time Time time.Time TimeDefault time.Time }

func main() { var t Timestamp

dec := NewDecoder(nil)

// for Timestamp field
dec.RegisterCustomType(func(vals []string) (interface{}, error) {
        return time.Parse("2006-01-02T15:04:05Z07:00", vals[0])
}, []interface{}{time.Time{}}, []interface{}{&t.Timestamp{}})

// for Time field
dec.RegisterCustomType(func(vals []string) (interface{}, error) {
            return time.Parse("Mon, 02 Jan 2006 15:04:05 MST", vals[0])
}, []interface{}{time.Time{}}, []interface{}{&t.Time{}})

// for field that not be Time or Timestamp, e.g. in this example, TimeDefault.
dec.RegisterCustomType(func(vals []string) (interface{}, error) {
            return time.Parse("2006-01-02", vals[0])
}, []interface{}{time.Time{}}, nil)

dec.Decode(url.Values{}, &t)

}

Notes

Version 2 is compatible with old syntax to access to maps (map.key), but brackets are the preferred way to access a map (map[key]).