Skip to content

Commit c91ccaf

Browse files
joeybloggsjoeybloggs
authored andcommitted
Add examples + godoc + updated README
1 parent 492703a commit c91ccaf

File tree

5 files changed

+421
-6
lines changed

5 files changed

+421
-6
lines changed

README.md

Lines changed: 194 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,194 @@
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+
![Project status](https://img.shields.io/badge/version-0.9-green.svg)
5+
[![GoDoc](https://github.com/go-playground/form?status.svg)](https://godoc.org/github.com/go-playground/form)
6+
![License](https://img.shields.io/dub/l/vibe-d.svg)
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.

doc.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
Package form parses url.Values and fills a struct with values, creating objects as necessary.
3+
4+
5+
It has the following features:
6+
7+
- Primitives types cause zero allocations.
8+
- Supports map of almost all types.
9+
- Supports both Numbered and Normal arrays i.e. "Array[0]" and just "Array" with multiple values passed.
10+
- Allows for Custom Type registration.
11+
- Handles time.Time using RFC3339 time format by default, but can easily be changes usings registering a Custom Type.
12+
13+
Common Questions
14+
15+
Questions
16+
17+
Does it support encoding.TextUnmarshaler?
18+
No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable.
19+
20+
Supported Types
21+
22+
out of the box supported types
23+
24+
- string
25+
- bool
26+
- int, int8, int16, int32, int64
27+
- uint, uint8, uint16, uint32, uint64
28+
- float32, float64
29+
- struct and anonymous struct
30+
- interface{}
31+
- time.Time` - by default using RFC3339
32+
- a `pointer` to one of the above types
33+
- slice, array
34+
- map
35+
- `custom types` can override any of the above types
36+
- many other types may be supported inherently (i.e. bson.ObjectId is type ObjectId string,
37+
which will get populated by the string type
38+
39+
**NOTE**: map, struct and slice nesting are ad infinitum.
40+
41+
Usage
42+
43+
symbols
44+
45+
- Use symbol `.` for separating fields/structs. (i.e, `structfield.field`)
46+
- Use `[index or key]` for access to index of a slice/array or key for map.
47+
(i.e, `arrayfield[0]`, `mapfield[keyvalue]`)
48+
49+
html
50+
51+
<form method="POST">
52+
<input type="text" name="Name" value="joeybloggs"/>
53+
<input type="text" name="Age" value="3"/>
54+
<input type="text" name="Gender" value="Male"/>
55+
<input type="text" name="Address[0].Name" value="26 Here Blvd."/>
56+
<input type="text" name="Address[0].Phone" value="9(999)999-9999"/>
57+
<input type="text" name="Address[1].Name" value="26 There Blvd."/>
58+
<input type="text" name="Address[1].Phone" value="1(111)111-1111"/>
59+
<input type="text" name="active" value="true"/>
60+
<input type="text" name="MapExample[key]" value="value"/>
61+
<input type="text" name="NestedMap[key][key]" value="value"/>
62+
<input type="text" name="NestedArray[0][0]" value="value"/>
63+
<input type="submit"/>
64+
</form>
65+
66+
Example
67+
68+
example parsing the above HTML
69+
70+
package main
71+
72+
import (
73+
"fmt"
74+
"log"
75+
"net/url"
76+
77+
"github.com/go-playground/form"
78+
)
79+
80+
// Address contains address information
81+
type Address struct {
82+
Name string
83+
Phone string
84+
}
85+
86+
// User contains user information
87+
type User struct {
88+
Name string
89+
Age uint8
90+
Gender string
91+
Address []Address
92+
Active bool `form:"active"`
93+
MapExample map[string]string
94+
NestedMap map[string]map[string]string
95+
NestedArray [][]string
96+
}
97+
98+
// use a single instance of Decoder, it caches struct info
99+
var decoder *form.Decoder
100+
101+
func main() {
102+
decoder = form.NewDecoder()
103+
104+
// this simulates the results of http.Request's ParseForm() function
105+
values := parseForm()
106+
107+
var user User
108+
109+
// must pass a pointer
110+
err := decoder.Decode(&user, values)
111+
if err != nil {
112+
log.Panic(err)
113+
}
114+
115+
fmt.Printf("%#v\n", user)
116+
}
117+
118+
// this simulates the results of http.Request's ParseForm() function
119+
func parseForm() url.Values {
120+
return url.Values{
121+
"Name": []string{"joeybloggs"},
122+
"Age": []string{"3"},
123+
"Gender": []string{"Male"},
124+
"Address[0].Name": []string{"26 Here Blvd."},
125+
"Address[0].Phone": []string{"9(999)999-9999"},
126+
"Address[1].Name": []string{"26 There Blvd."},
127+
"Address[1].Phone": []string{"1(111)111-1111"},
128+
"active": []string{"true"},
129+
"MapExample[key]": []string{"value"},
130+
"NestedMap[key][key]": []string{"value"},
131+
"NestedArray[0][0]": []string{"value"},
132+
}
133+
}
134+
135+
136+
Registering Custom Types
137+
138+
can easily register custom types.
139+
140+
decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
141+
return time.Parse("2006-01-02", vals[0])
142+
}, time.Time{})
143+
*/
144+
package form

0 commit comments

Comments
 (0)