top

Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.

It has the following features:

- Primitives types cause zero allocations.
- Supports map of almost all types.
- Supports both Numbered and Normal arrays eg. "Array[0]" and just "Array"
  with multiple values passed.
- Array honours the specified index. eg. if "Array[2]" is the only Array
  value passed down, it will be put at index 2; if array isn't big enough
  it will be expanded.
- Only creates objects as necessary eg. if no `array` or `map` values are
  passed down, the `array` and `map` are left as their default values in
  the struct.
- Allows for Custom Type registration.
- Handles time.Time using RFC3339 time format by default,
  but can easily be changed by registering a Custom Type, see below.
- Handles Encoding & Decoding of almost all Go types eg. can Decode into
  struct, array, map, int... and Encode a struct, array, map, int...

Common Questions

Questions

Does it support encoding.TextUnmarshaler?
No because TextUnmarshaler only accepts []byte but posted values can have
multiple values, so is not suitable.

Supported Types

out of the box supported types

- string
- bool
- int, int8, int16, int32, int64
- uint, uint8, uint16, uint32, uint64
- float32, float64
- struct and anonymous struct
- interface{}
- time.Time` - by default using RFC3339
- a `pointer` to one of the above types
- slice, array
- map
- `custom types` can override any of the above types
- many other types may be supported inherently (eg. bson.ObjectId is
  type ObjectId string, which will get populated by the string type

**NOTE**: map, struct and slice nesting are ad infinitum.

Usage

symbols

- Use symbol `.` for separating fields/structs. (eg. `structfield.field`)
- Use `[index or key]` for access to index of a slice/array or key for map.
  (eg. `arrayfield[0]`, `mapfield[keyvalue]`)

html

<form method="POST">
    <input type="text" name="Name" value="joeybloggs"/>
    <input type="text" name="Age" value="3"/>
    <input type="text" name="Gender" value="Male"/>
    <input type="text" name="Address[0].Name" value="26 Here Blvd."/>
    <input type="text" name="Address[0].Phone" value="9(999)999-9999"/>
    <input type="text" name="Address[1].Name" value="26 There Blvd."/>
    <input type="text" name="Address[1].Phone" value="1(111)111-1111"/>
    <input type="text" name="active" value="true"/>
    <input type="text" name="MapExample[key]" value="value"/>
    <input type="text" name="NestedMap[key][key]" value="value"/>
    <input type="text" name="NestedArray[0][0]" value="value"/>
    <input type="submit"/>
</form>

Example

example decoding the above HTML

package main

import (
    "fmt"
    "log"
    "net/url"

    "github.com/go-playground/form"
)

// Address contains address information
type Address struct {
    Name  string
    Phone string
}

// User contains user information
type User struct {
    Name        string
    Age         uint8
    Gender      string
    Address     []Address
    Active      bool `form:"active"`
    MapExample  map[string]string
    NestedMap   map[string]map[string]string
    NestedArray [][]string
}

// use a single instance of Decoder, it caches struct info
var decoder *form.Decoder

func main() {
    decoder = form.NewDecoder()

    // this simulates the results of http.Request's ParseForm() function
    values := parseForm()

    var user User

    // must pass a pointer
    err := decoder.Decode(&user, values)
    if err != nil {
        log.Panic(err)
    }

    fmt.Printf("%#v\n", user)
}

// this simulates the results of http.Request's ParseForm() function
func parseForm() url.Values {
    return url.Values{
        "Name":                []string{"joeybloggs"},
        "Age":                 []string{"3"},
        "Gender":              []string{"Male"},
        "Address[0].Name":     []string{"26 Here Blvd."},
        "Address[0].Phone":    []string{"9(999)999-9999"},
        "Address[1].Name":     []string{"26 There Blvd."},
        "Address[1].Phone":    []string{"1(111)111-1111"},
        "active":              []string{"true"},
        "MapExample[key]":     []string{"value"},
        "NestedMap[key][key]": []string{"value"},
        "NestedArray[0][0]":   []string{"value"},
    }
}

example encoding

package main

import (
    "fmt"
    "log"

    "github.com/go-playground/form"
)

// Address contains address information
type Address struct {
    Name  string
    Phone string
}

// User contains user information
type User struct {
    Name        string
    Age         uint8
    Gender      string
    Address     []Address
    Active      bool `form:"active"`
    MapExample  map[string]string
    NestedMap   map[string]map[string]string
    NestedArray [][]string
}

// use a single instance of Encoder, it caches struct info
var encoder *form.Encoder

func main() {
    encoder = form.NewEncoder()

    user := User{
        Name:   "joeybloggs",
        Age:    3,
        Gender: "Male",
        Address: []Address{
            {Name: "26 Here Blvd.", Phone: "9(999)999-9999"},
            {Name: "26 There Blvd.", Phone: "1(111)111-1111"},
        },
        Active:      true,
        MapExample:  map[string]string{"key": "value"},
        NestedMap:   map[string]map[string]string{"key": {"key": "value"}},
        NestedArray: [][]string{{"value"}},
    }

    // must pass a pointer
    values, err := encoder.Encode(&user)
    if err != nil {
        log.Panic(err)
    }

    fmt.Printf("%#v\n", values)
}

Registering Custom Types

Decoder

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

Encoder

encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
        return []string{x.(time.Time).Format("2006-01-02")}, nil
    }, time.Time{})

Ignoring Fields

you can tell form to ignore fields using `-` in the tag

type MyStruct struct {
    Field string `form:"-"`
}

Notes

To maximize compatibility with other systems the Encoder attempts to avoid using array indexes in url.Values if at all possible.

eg.

// A struct field of
Field []string{"1", "2", "3"}

// will be output a url.Value as
"Field": []string{"1", "2", "3"}

and not
"Field[0]": []string{"1"}
"Field[1]": []string{"2"}
"Field[2]": []string{"3"}

// however there are times where it is unavoidable, like with pointers
i := int(1)
Field []*string{nil, nil, &i}

// to avoid index 1 and 2 must use index
"Field[2]": []string{"1"}

Imported by 7 package(s)

  1. github.com/go-experimental/lars
  2. github.com/go-experimental/lcars
  3. github.com/go-playground/lars
  4. github.com/go-playground/pure
  5. github.com/themihai/form/examples/decoder
  6. github.com/themihai/form/examples/encoder
  7. github.com/vkolev/form/examples

Imported only in test by 1 package(s)

  1. github.com/PuerkitoBio/httpparms

Test imports 1 package(s)

  1. gopkg.in/go-playground/assert.v1