|
1 | | -# form |
2 | | -form to struct decoder |
| 1 | +Package form |
| 2 | +============ |
| 3 | +<img align="right" src="https://raw.githubusercontent.com/go-playground/form/master/logo.jpg"> |
| 4 | + |
| 5 | +[](https://godoc.org/github.com/go-playground/form) |
| 6 | + |
| 7 | + |
| 8 | +Package form parses url.Values and fills a struct with values, creating objects as necessary. |
| 9 | + |
| 10 | +It has the following features: |
| 11 | + |
| 12 | +- Primitives types cause zero allocations. |
| 13 | +- Supports map of almost all types. |
| 14 | +- Supports both Numbered and Normal arrays i.e. "Array[0]" and just "Array" with multiple values passed. |
| 15 | +- Allows for Custom Type registration. |
| 16 | +- Handles time.Time using RFC3339 time format by default, but can easily be changes usings registering a Custom Type. |
| 17 | + |
| 18 | +Common Questions |
| 19 | + |
| 20 | +- Does it support encoding.TextUnmarshaler? No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable. |
| 21 | + |
| 22 | +Supported Types ( out of the box ) |
| 23 | +---------- |
| 24 | + |
| 25 | +* `string` |
| 26 | +* `bool` |
| 27 | +* `int`, `int8`, `int16`, `int32`, `int64` |
| 28 | +* `uint`, `uint8`, `uint16`, `uint32`, `uint64` |
| 29 | +* `float32`, `float64` |
| 30 | +* `struct` and `anonymous struct` |
| 31 | +* `interface{}` |
| 32 | +* `time.Time` - by default using RFC3339 |
| 33 | +* a `pointer` to one of the above types |
| 34 | +* `slice`, `array` |
| 35 | +* `map` |
| 36 | +* `custom types` can override any of the above types |
| 37 | +* many other types may be supported inherently (i.e. bson.ObjectId is type ObjectId string, which will get populated by the string type |
| 38 | + |
| 39 | +**NOTE**: `map`, `struct` and `slice` nesting are ad infinitum. |
| 40 | + |
| 41 | +Installation |
| 42 | +------------ |
| 43 | + |
| 44 | +Use go get. |
| 45 | + |
| 46 | + go get github.com/go-playground/form |
| 47 | + |
| 48 | +Then import the form package into your own code. |
| 49 | + |
| 50 | + import "github.com/go-playground/form" |
| 51 | + |
| 52 | +Usage |
| 53 | +----- |
| 54 | + |
| 55 | +- Use symbol `.` for separating fields/structs. (i.e. `structfield.field`) |
| 56 | +- Use `[index or key]` for access to index of a slice/array or key for map. (i.e. `arrayfield[0]`, `mapfield[keyvalue]`) |
| 57 | + |
| 58 | +```html |
| 59 | +<form method="POST"> |
| 60 | + <input type="text" name="Name" value="joeybloggs"/> |
| 61 | + <input type="text" name="Age" value="3"/> |
| 62 | + <input type="text" name="Gender" value="Male"/> |
| 63 | + <input type="text" name="Address[0].Name" value="26 Here Blvd."/> |
| 64 | + <input type="text" name="Address[0].Phone" value="9(999)999-9999"/> |
| 65 | + <input type="text" name="Address[1].Name" value="26 There Blvd."/> |
| 66 | + <input type="text" name="Address[1].Phone" value="1(111)111-1111"/> |
| 67 | + <input type="text" name="active" value="true"/> |
| 68 | + <input type="text" name="MapExample[key]" value="value"/> |
| 69 | + <input type="text" name="NestedMap[key][key]" value="value"/> |
| 70 | + <input type="text" name="NestedArray[0][0]" value="value"/> |
| 71 | + <input type="submit"/> |
| 72 | +</form> |
| 73 | +``` |
| 74 | + |
| 75 | +Example |
| 76 | +------- |
| 77 | +```go |
| 78 | +package main |
| 79 | + |
| 80 | +import ( |
| 81 | + "fmt" |
| 82 | + "log" |
| 83 | + "net/url" |
| 84 | + |
| 85 | + "github.com/go-playground/form" |
| 86 | +) |
| 87 | + |
| 88 | +// Address contains address information |
| 89 | +type Address struct { |
| 90 | + Name string |
| 91 | + Phone string |
| 92 | +} |
| 93 | + |
| 94 | +// User contains user information |
| 95 | +type User struct { |
| 96 | + Name string |
| 97 | + Age uint8 |
| 98 | + Gender string |
| 99 | + Address []Address |
| 100 | + Active bool `form:"active"` |
| 101 | + MapExample map[string]string |
| 102 | + NestedMap map[string]map[string]string |
| 103 | + NestedArray [][]string |
| 104 | +} |
| 105 | + |
| 106 | +// use a single instance of Decoder, it caches struct info |
| 107 | +var decoder *form.Decoder |
| 108 | + |
| 109 | +func main() { |
| 110 | + decoder = form.NewDecoder() |
| 111 | + |
| 112 | + // this simulates the results of http.Request's ParseForm() function |
| 113 | + values := parseForm() |
| 114 | + |
| 115 | + var user User |
| 116 | + |
| 117 | + // must pass a pointer |
| 118 | + err := decoder.Decode(&user, values) |
| 119 | + if err != nil { |
| 120 | + log.Panic(err) |
| 121 | + } |
| 122 | + |
| 123 | + fmt.Printf("%#v\n", user) |
| 124 | +} |
| 125 | + |
| 126 | +// this simulates the results of http.Request's ParseForm() function |
| 127 | +func parseForm() url.Values { |
| 128 | + return url.Values{ |
| 129 | + "Name": []string{"joeybloggs"}, |
| 130 | + "Age": []string{"3"}, |
| 131 | + "Gender": []string{"Male"}, |
| 132 | + "Address[0].Name": []string{"26 Here Blvd."}, |
| 133 | + "Address[0].Phone": []string{"9(999)999-9999"}, |
| 134 | + "Address[1].Name": []string{"26 There Blvd."}, |
| 135 | + "Address[1].Phone": []string{"1(111)111-1111"}, |
| 136 | + "active": []string{"true"}, |
| 137 | + "MapExample[key]": []string{"value"}, |
| 138 | + "NestedMap[key][key]": []string{"value"}, |
| 139 | + "NestedArray[0][0]": []string{"value"}, |
| 140 | + } |
| 141 | +} |
| 142 | +``` |
| 143 | + |
| 144 | +Registering Custom Types |
| 145 | +-------------- |
| 146 | +```go |
| 147 | +decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) { |
| 148 | + return time.Parse("2006-01-02", vals[0]) |
| 149 | + }, time.Time{}) |
| 150 | +``` |
| 151 | + |
| 152 | +Benchmarks |
| 153 | +------ |
| 154 | +###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.6.2 darwin/amd64 |
| 155 | + |
| 156 | +NOTE: the 1 allocationand B/op in the first 4 is actually the struct allocating when passing it in, so primitives are actually zero allocation. |
| 157 | + |
| 158 | +```go |
| 159 | +go test -bench=. -benchmem=true |
| 160 | + |
| 161 | +PASS |
| 162 | +BenchmarkSimpleUserStruct-8 5000000 320 ns/op 64 B/op 1 allocs/op |
| 163 | +BenchmarkSimpleUserStructParallel-8 20000000 110 ns/op 64 B/op 1 allocs/op |
| 164 | +BenchmarkPrimitivesStructAllPrimitivesTypes-8 1000000 1075 ns/op 96 B/op 1 allocs/op |
| 165 | +BenchmarkPrimitivesStructAllPrimitivesTypesParallel-8 5000000 285 ns/op 96 B/op 1 allocs/op |
| 166 | +BenchmarkComplexArrayStructAllTypes-8 100000 19748 ns/op 6600 B/op 143 allocs/op |
| 167 | +BenchmarkComplexArrayStructAllTypesParallel-8 300000 5844 ns/op 6600 B/op 143 allocs/op |
| 168 | +BenchmarkComplexMapStructAllTypes-8 50000 33943 ns/op 20276 B/op 224 allocs/op |
| 169 | +BenchmarkComplexMapStructAllTypesParallel-8 200000 12228 ns/op 20277 B/op 224 allocs/op |
| 170 | +BenchmarkArrayMapNestedStruct-8 300000 4658 ns/op 1840 B/op 27 allocs/op |
| 171 | +BenchmarkArrayMapNestedStructParallel-8 1000000 1498 ns/op 1840 B/op 27 allocs/op |
| 172 | +``` |
| 173 | + |
| 174 | + |
| 175 | +Complimentary Software |
| 176 | +---------------------- |
| 177 | + |
| 178 | +Here is a list of software that compliments using this library post decoding. |
| 179 | + |
| 180 | +* [Validator](https://github.com/go-playground/validator) - Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving. |
| 181 | +* [Conform](https://github.com/leebenson/conform) - Trims, sanitizes & scrubs data based on struct tags. |
| 182 | + |
| 183 | +Package Versioning |
| 184 | +---------- |
| 185 | +I'm jumping on the vendoring bandwagon, you should vendor this package as I will not |
| 186 | +be creating different version with gopkg.in like allot of my other libraries. |
| 187 | + |
| 188 | +Why? because my time is spread pretty thin maintaining all of the libraries I have + LIFE, |
| 189 | +it is so freeing not to worry about it and will help me keep pouring out bigger and better |
| 190 | +things for you the community. |
| 191 | + |
| 192 | +License |
| 193 | +------ |
| 194 | +Distributed under MIT License, please see license file in code for more details. |
0 commit comments