包形式解码url。将值输入value(s)和编码将值输入url.Values。
具有以下特点:
- 支持几乎所有类型的映射。
- 同时支持编号数组和普通数组。“数组[0]”和只是“数组”的多个值传递。
- Slice尊重指定的索引。如。如果“Slice[2]”是传递下来的唯一的Slice值,那么它将被放在索引2处;如果slice不够大,它将被扩展。
- 数组尊重指定的索引。如。如果“数组[2]”是唯一传递下来的数组值,它将被放在下标2处;如果数组不够大,将打印警告并忽略值。
- 只在必要时创建对象。如果没有传递数组或映射值,则数组和映射在结构中保留默认值。
- 允许自定义类型注册。
- 处理时间。时间默认使用RFC3339时间格式,但是可以通过注册自定义类型轻松更改,见下面。
- 处理编码和解码的几乎所有的Go类型。可以解码成结构,数组,映射,int…编码struct, array, map, int…
常见问题
- 它支持编码。textunmarshaler吗?不,因为TextUnmarshaler只接受[] byte,但是发布的值可以有多个值,所以不合适。
- array/slice 与 array[idx]/slice[idx]的混合,它们是按照什么顺序解析的?array/slice然后array[idx]/slice[idx]
支持的类型(开箱即用)
string
bool
int
,int8
,int16
,int32
,int64
uint
,uint8
,uint16
,uint32
,uint64
float32
,float64
struct
andanonymous struct
interface{}
time.Time
- by default using RFC3339- a
pointer
to one of the above types slice
,array
map
custom types
可以覆盖上面的任何类型- 可能天生就支持许多其他类型
注意:map、struct和slice嵌套是无限的。
安装
使用 go get
go get github.com/go-playground/form
然后将表单包导入到自己的代码中。
import "github.com/go-playground/form/v4"
使用
- 使用符号。分隔fields/structs。(如。structfield.field)
- 使用[索引或键]访问一个slice/array的索引,或使用键访问map。(如。arrayfield [0], mapfield [keyvalue])
<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>
例子
解码
package main
import (
"fmt"
"log"
"net/url"
"github.com/go-playground/form/v4"
)
// 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
//这将模拟http的结果。请求的ParseForm()函数
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
//这将模拟http的结果。请求的ParseForm()函数
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"},
}
}
编码
package main
import (
"fmt"
"log"
"github.com/go-playground/form/v4"
)
// Address contains address information
//地址包含地址信息
type Address struct {
Name string
Phone string
}
// User contains user information
//User包含用户信息
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)
}
注册自定义类型
解码器
decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
return time.Parse("2006-01-02", vals[0])
}, time.Time{})
附加:如果注册了一个结构类型,该函数将只在url时被调用。值为结构而存在,而不仅仅是结构字段。url。value {“User”:”Name%3Djoeybloggs”}将调用以’User’为类型的自定义类型函数,但是url.Values{“User.Name”:”joeybloggs”}不会。
编码器
encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
return []string{x.(time.Time).Format("2006-01-02")}, nil
}, time.Time{})
忽略字段
您可以告诉form在标记中使用-忽略字段
type MyStruct struct {
Field string `form:"-"`
}
忽略空值
您可以告诉form在标签中使用omitempty或FieldName omitempty来省略空字段
type MyStruct struct {
Field string `form:",omitempty"`
Field2 string `form:"CustomFieldName,omitempty"`
}
注意
为了最大化与其他系统的兼容性,编码器试图避免在url中使用数组索引。值,如果可能的话。
例如
// 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"}
基准测试程序数值
注意:前4个解码中的1分配和B/op实际上是传递它时的结构分配,所以原语实际上是零分配。
go test -run=NONE -bench=. -benchmem=true
goos: darwin
goarch: amd64
pkg: github.com/go-playground/form/benchmarks
BenchmarkSimpleUserDecodeStruct-8 5000000 236 ns/op 64 B/op 1 allocs/op
BenchmarkSimpleUserDecodeStructParallel-8 20000000 82.1 ns/op 64 B/op 1 allocs/op
BenchmarkSimpleUserEncodeStruct-8 2000000 627 ns/op 485 B/op 10 allocs/op
BenchmarkSimpleUserEncodeStructParallel-8 10000000 223 ns/op 485 B/op 10 allocs/op
BenchmarkPrimitivesDecodeStructAllPrimitivesTypes-8 2000000 724 ns/op 96 B/op 1 allocs/op
BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-8 10000000 246 ns/op 96 B/op 1 allocs/op
BenchmarkPrimitivesEncodeStructAllPrimitivesTypes-8 500000 3187 ns/op 2977 B/op 36 allocs/op
BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-8 1000000 1106 ns/op 2977 B/op 36 allocs/op
BenchmarkComplexArrayDecodeStructAllTypes-8 100000 13748 ns/op 2248 B/op 121 allocs/op
BenchmarkComplexArrayDecodeStructAllTypesParallel-8 500000 4313 ns/op 2249 B/op 121 allocs/op
BenchmarkComplexArrayEncodeStructAllTypes-8 200000 10758 ns/op 7113 B/op 104 allocs/op
BenchmarkComplexArrayEncodeStructAllTypesParallel-8 500000 3532 ns/op 7113 B/op 104 allocs/op
BenchmarkComplexMapDecodeStructAllTypes-8 100000 17644 ns/op 5305 B/op 130 allocs/op
BenchmarkComplexMapDecodeStructAllTypesParallel-8 300000 5470 ns/op 5308 B/op 130 allocs/op
BenchmarkComplexMapEncodeStructAllTypes-8 200000 11155 ns/op 6971 B/op 129 allocs/op
BenchmarkComplexMapEncodeStructAllTypesParallel-8 500000 3768 ns/op 6971 B/op 129 allocs/op
BenchmarkDecodeNestedStruct-8 500000 2462 ns/op 384 B/op 14 allocs/op
BenchmarkDecodeNestedStructParallel-8 2000000 814 ns/op 384 B/op 14 allocs/op
BenchmarkEncodeNestedStruct-8 1000000 1483 ns/op 693 B/op 16 allocs/op
BenchmarkEncodeNestedStructParallel-8 3000000 525 ns/op 693 B/op 16 allocs/op
竞争对手的基准可以在这里找到
免费软件
下面是一个软件列表,它们使用这个库进行解码。