golang表单数据解码到结构体的高效插件库formam的使用
Golang表单数据解码到结构体的高效插件库formam的使用
formam是一个用于解码HTTP表单和查询参数的Go包,最低要求Go 1.12或更高版本。
安装
go get github.com/monoculum/formam/v3
特性
- 支持
maps
、structs
和slices
的无限嵌套 - 支持在值和map键中使用
UnmarshalText()
接口 - 支持的map键类型包括:
string
、int
及其变体、uint
及其变体、uintptr
、float32
、float64
、bool
、struct
、custom types
等 - 可以访问值为
map
、struct
或slice
的interface{}
字段 - 通过
UnmarshalText()
方法解码格式为2006-01-02
的time.Time
- 解码
url.URL
- 无需显式指定索引即可追加到
slice
和array
类型 - 为自定义类型注册函数
基本使用示例
在HTML表单中
- 使用
.
访问结构体字段(如struct.field1
) - 使用
[<index>]
访问特定的slice/array索引(如struct.array[0]
),追加数据时不需要添加索引 - 使用
[<key>]
访问map键(如struct.map[es-ES]
)
<form method="POST">
<input type="text" name="Name" value="Sony" />
<input type="text" name="Location.Country" value="Japan" />
<input type="text" name="Location.City" value="Tokyo" />
<input type="text" name="Products[0].Name" value="Playstation 4" />
<input type="text" name="Products[0].Type" value="Video games" />
<input type="text" name="Products[1].Name" value="TV Bravia 32" />
<input type="text" name="Products[1].Type" value="TVs" />
<input type="text" name="Founders[0]" value="Masaru Ibuka" />
<input type="text" name="Founders[0]" value="Akio Morita" />
<input type="text" name="Employees" value="90000" />
<input type="text" name="public" value="true" />
<input type="url" name="website" value="http://www.sony.net" />
<input type="date" name="foundation" value="1946-05-07" />
<input type="text" name="Interface.ID" value="12" />
<input type="text" name="Interface.Name" value="Go Programming Language" />
<input type="submit" />
</form>
在Go中
可以使用formam
结构体标签确保表单值正确解组到结构体字段中。
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{
// 尽管是接口字段,也可以访问其字段!
Interface: &InterfaceStruct{},
}
dec := formam.NewDecoder(&formam.DecoderOptions{TagName: "formam"})
return dec.Decode(r.Form, &m)
}
支持的类型
目标结构体中支持的类型包括:
string
bool
int
,int8
,int16
,int32
,int64
uint
,uint8
,uint16
,uint32
,uint64
float32
,float64
slice
,array
struct
和struct anonymous
map
interface{}
time.Time
url.URL
custom types
到上述类型之一- 上述类型的
pointer
自定义解组
可以通过实现encoding.TextUnmarshaler
接口来解组数据和map键。
如果表单发送多个值,则只有第一个值传递给UnmarshalText()
,但如果名称以[]
结尾,则会为所有值调用它。
自定义类型
可以使用RegisterCustomType()
方法为自定义类型注册函数。这将适用于给定数量的字段或具有给定类型的所有字段。
除非使用PrefUnmarshalText
选项,否则注册类型优先于UnmarshalText方法。
所有字段
decoder.RegisterCustomType(func(vals []string) (interface{}, error) {
return time.Parse("2006-01-02", vals[0])
}, []interface{}{time.Time{}}, nil)
特定字段
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)
}
注意事项
版本2兼容旧的map访问语法(map.key
),但括号是访问map的首选方式(map[key]
)。
更多关于golang表单数据解码到结构体的高效插件库formam的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang表单数据解码到结构体的高效插件库formam的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang表单数据解码到结构体的高效插件库 - formam
formam 是一个轻量级的 Go 库,用于将 HTML 表单数据解码到 Go 结构体中。它比标准库的 encoding/json
和 encoding/xml
更专注于表单数据的处理,提供了更灵活的解码方式。
formam 的主要特点
- 支持将表单数据解码到结构体、切片和数组
- 支持嵌套结构体
- 支持自定义类型转换
- 支持忽略字段
- 简单易用的 API
安装 formam
go get github.com/monoculum/formam
基本使用示例
package main
import (
"fmt"
"net/url"
"github.com/monoculum/formam"
)
type User struct {
Name string
Age int
Email string
Active bool
Hobbies []string
}
func main() {
// 模拟表单数据
values := url.Values{}
values.Set("Name", "John Doe")
values.Set("Age", "30")
values.Set("Email", "john@example.com")
values.Set("Active", "true")
values.Add("Hobbies", "reading")
values.Add("Hobbies", "coding")
values.Add("Hobbies", "swimming")
var user User
// 创建 formam 解码器
decoder := formam.NewDecoder(&formam.DecoderOptions{
TagName: "formam", // 自定义标签名,默认为 "form"
})
// 解码表单数据到结构体
err := decoder.Decode(values, &user)
if err != nil {
fmt.Println("解码错误:", err)
return
}
fmt.Printf("%+v\n", user)
// 输出: {Name:John Doe Age:30 Email:john@example.com Active:true Hobbies:[reading coding swimming]}
}
高级功能示例
1. 嵌套结构体
type Address struct {
Street string
City string
Country string
}
type Person struct {
Name string
Age int
Address Address `formam:"address"` // 使用自定义标签
}
func nestedStructExample() {
values := url.Values{}
values.Set("Name", "Alice")
values.Set("Age", "25")
values.Set("address.Street", "123 Main St")
values.Set("address.City", "New York")
values.Set("address.Country", "USA")
var person Person
decoder := formam.NewDecoder(nil)
err := decoder.Decode(values, &person)
if err != nil {
fmt.Println("解码错误:", err)
return
}
fmt.Printf("%+v\n", person)
// 输出: {Name:Alice Age:25 Address:{Street:123 Main St City:New York Country:USA}}
}
2. 忽略字段
type Config struct {
Username string `formam:"username"`
Password string `formam:"-"` // 使用 "-" 忽略该字段
Token string
}
func ignoreFieldExample() {
values := url.Values{}
values.Set("username", "admin")
values.Set("Password", "secret")
values.Set("Token", "abc123")
var config Config
decoder := formam.NewDecoder(nil)
err := decoder.Decode(values, &config)
if err != nil {
fmt.Println("解码错误:", err)
return
}
fmt.Printf("%+v\n", config)
// 输出: {Username:admin Password: Token:abc123}
}
3. 自定义类型转换
type CustomTime struct {
time.Time
}
func (t *CustomTime) Set(value interface{}) error {
if str, ok := value.(string); ok {
parsed, err := time.Parse("2006-01-02", str)
if err != nil {
return err
}
t.Time = parsed
return nil
}
return fmt.Errorf("无法转换类型")
}
type Event struct {
Title string `formam:"title"`
Date CustomTime `formam:"date"`
}
func customTypeExample() {
values := url.Values{}
values.Set("title", "Go Conference")
values.Set("date", "2023-11-15")
var event Event
decoder := formam.NewDecoder(nil)
err := decoder.Decode(values, &event)
if err != nil {
fmt.Println("解码错误:", err)
return
}
fmt.Printf("Event: %s on %v\n", event.Title, event.Date.Format("January 2, 2006"))
// 输出: Event: Go Conference on November 15, 2023
}
性能考虑
formam 在设计时就考虑了性能因素,它比标准库的 url.Values
解析更高效,特别是在处理复杂嵌套结构时。以下是一些性能优化建议:
- 复用解码器实例而不是每次都创建新的
- 对于频繁使用的结构体,可以考虑预先生成解码器
- 避免在热路径中频繁创建和销毁解码器
与标准库的比较
与标准库的 net/url
和 encoding/json
相比,formam 提供了更直观的表单数据处理方式:
- 更简单的嵌套结构支持
- 更灵活的类型转换
- 更直观的表单字段到结构体字段的映射
- 更好的错误处理
总结
formam 是一个简单而强大的 Go 库,专门用于处理 HTML 表单数据到 Go 结构体的解码。它提供了比标准库更灵活和直观的 API,特别适合 Web 开发中处理表单数据。通过支持嵌套结构、自定义类型转换和字段忽略等功能,formam 可以大大简化表单处理的代码。
对于需要处理复杂表单数据的 Go Web 应用,formam 是一个值得考虑的选择。