{"id":13366420,"url":"https://github.com/Go-playground/form","last_synced_at":"2025-03-12T18:30:53.972Z","repository":{"id":44155607,"uuid":"59752363","full_name":"go-playground/form","owner":"go-playground","description":":steam_locomotive: Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.","archived":false,"fork":false,"pushed_at":"2024-05-24T00:37:26.000Z","size":248,"stargazers_count":767,"open_issues_count":13,"forks_count":42,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-10-25T05:22:30.457Z","etag":null,"topics":["decoding","encoder","form","form-data","parser"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/go-playground.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-05-26T13:26:40.000Z","updated_at":"2024-10-22T21:00:45.000Z","dependencies_parsed_at":"2024-06-18T12:27:16.221Z","dependency_job_id":"8ae323b7-02fa-4dec-88b5-f4730e8f0940","html_url":"https://github.com/go-playground/form","commit_stats":{"total_commits":116,"total_committers":10,"mean_commits":11.6,"dds":"0.19827586206896552","last_synced_commit":"8785d3ce031b7f042da48a63a4ea5b03cae12f6f"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-playground%2Fform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-playground%2Fform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-playground%2Fform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-playground%2Fform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/go-playground","download_url":"https://codeload.github.com/go-playground/form/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243271151,"owners_count":20264400,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decoding","encoder","form","form-data","parser"],"created_at":"2024-07-30T00:01:24.539Z","updated_at":"2025-03-12T18:30:53.657Z","avatar_url":"https://github.com/go-playground.png","language":"Go","readme":"Package form\n============\n\u003cimg align=\"right\" src=\"https://raw.githubusercontent.com/go-playground/form/master/logo.jpg\"\u003e![Project status](https://img.shields.io/badge/version-4.2.1-green.svg)\n[![Build Status](https://github.com/go-playground/form/actions/workflows/workflow.yml/badge.svg)](https://github.com/go-playground/form/actions/workflows/workflow.yml)\n[![Coverage Status](https://coveralls.io/repos/github/go-playground/form/badge.svg?branch=master)](https://coveralls.io/github/go-playground/form?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/form)](https://goreportcard.com/report/github.com/go-playground/form)\n[![GoDoc](https://godoc.org/github.com/go-playground/form?status.svg)](https://godoc.org/github.com/go-playground/form)\n![License](https://img.shields.io/dub/l/vibe-d.svg)\n[![Gitter](https://badges.gitter.im/go-playground/form.svg)](https://gitter.im/go-playground/form?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge)\n\nPackage form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.\n\nIt has the following features:\n\n- Supports map of almost all types.\n- Supports both Numbered and Normal arrays eg. `\"Array[0]\"` and just `\"Array\"` with multiple values passed.\n- Slice honours the specified index. eg. if \"Slice[2]\" is the only Slice value passed down, it will be put at index 2; if slice isn't big enough it will be expanded.\n- 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 a warning will be printed and value ignored.\n- 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.\n- Allows for Custom Type registration.\n- Handles time.Time using RFC3339 time format by default, but can easily be changed by registering a Custom Type, see below.\n- Handles Encoding \u0026 Decoding of almost all Go types eg. can Decode into struct, array, map, int... and Encode a struct, array, map, int...\n\nCommon Questions\n\n- Does it support encoding.TextUnmarshaler? No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable.\n- Mixing `array/slice` with `array[idx]/slice[idx]`, in which order are they parsed? `array/slice` then `array[idx]/slice[idx]`\n\nSupported Types ( out of the box )\n----------\n\n* `string`\n* `bool`\n* `int`, `int8`, `int16`, `int32`, `int64`\n* `uint`, `uint8`, `uint16`, `uint32`, `uint64`\n* `float32`, `float64`\n* `struct` and `anonymous struct`\n* `interface{}`\n* `time.Time` - by default using RFC3339\n* a `pointer` to one of the above types\n* `slice`, `array`\n* `map`\n* `custom types` can override any of the above types\n* many other types may be supported inherently\n\n**NOTE**: `map`, `struct` and `slice` nesting are ad infinitum.\n\nInstallation\n------------\n\nUse go get.\n\n\tgo get github.com/go-playground/form\n\nThen import the form package into your own code.\n\n\timport \"github.com/go-playground/form/v4\"\n\nUsage\n-----\n\n- Use symbol `.` for separating fields/structs. (eg. `structfield.field`)\n- Use `[index or key]` for access to index of a slice/array or key for map. (eg. `arrayfield[0]`, `mapfield[keyvalue]`)\n\n```html\n\u003cform method=\"POST\"\u003e\n  \u003cinput type=\"text\" name=\"Name\" value=\"joeybloggs\"/\u003e\n  \u003cinput type=\"text\" name=\"Age\" value=\"3\"/\u003e\n  \u003cinput type=\"text\" name=\"Gender\" value=\"Male\"/\u003e\n  \u003cinput type=\"text\" name=\"Address[0].Name\" value=\"26 Here Blvd.\"/\u003e\n  \u003cinput type=\"text\" name=\"Address[0].Phone\" value=\"9(999)999-9999\"/\u003e\n  \u003cinput type=\"text\" name=\"Address[1].Name\" value=\"26 There Blvd.\"/\u003e\n  \u003cinput type=\"text\" name=\"Address[1].Phone\" value=\"1(111)111-1111\"/\u003e\n  \u003cinput type=\"text\" name=\"active\" value=\"true\"/\u003e\n  \u003cinput type=\"text\" name=\"MapExample[key]\" value=\"value\"/\u003e\n  \u003cinput type=\"text\" name=\"NestedMap[key][key]\" value=\"value\"/\u003e\n  \u003cinput type=\"text\" name=\"NestedArray[0][0]\" value=\"value\"/\u003e\n  \u003cinput type=\"submit\"/\u003e\n\u003c/form\u003e\n```\n\nExamples\n-------\n\nDecoding\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/url\"\n\n\t\"github.com/go-playground/form/v4\"\n)\n\n// Address contains address information\ntype Address struct {\n\tName  string\n\tPhone string\n}\n\n// User contains user information\ntype User struct {\n\tName        string\n\tAge         uint8\n\tGender      string\n\tAddress     []Address\n\tActive      bool `form:\"active\"`\n\tMapExample  map[string]string\n\tNestedMap   map[string]map[string]string\n\tNestedArray [][]string\n}\n\n// use a single instance of Decoder, it caches struct info\nvar decoder *form.Decoder\n\nfunc main() {\n\tdecoder = form.NewDecoder()\n\n\t// this simulates the results of http.Request's ParseForm() function\n\tvalues := parseForm()\n\n\tvar user User\n\n\t// must pass a pointer\n\terr := decoder.Decode(\u0026user, values)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfmt.Printf(\"%#v\\n\", user)\n}\n\n// this simulates the results of http.Request's ParseForm() function\nfunc parseForm() url.Values {\n\treturn url.Values{\n\t\t\"Name\":                []string{\"joeybloggs\"},\n\t\t\"Age\":                 []string{\"3\"},\n\t\t\"Gender\":              []string{\"Male\"},\n\t\t\"Address[0].Name\":     []string{\"26 Here Blvd.\"},\n\t\t\"Address[0].Phone\":    []string{\"9(999)999-9999\"},\n\t\t\"Address[1].Name\":     []string{\"26 There Blvd.\"},\n\t\t\"Address[1].Phone\":    []string{\"1(111)111-1111\"},\n\t\t\"active\":              []string{\"true\"},\n\t\t\"MapExample[key]\":     []string{\"value\"},\n\t\t\"NestedMap[key][key]\": []string{\"value\"},\n\t\t\"NestedArray[0][0]\":   []string{\"value\"},\n\t}\n}\n```\n\nEncoding\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/go-playground/form/v4\"\n)\n\n// Address contains address information\ntype Address struct {\n\tName  string\n\tPhone string\n}\n\n// User contains user information\ntype User struct {\n\tName        string\n\tAge         uint8\n\tGender      string\n\tAddress     []Address\n\tActive      bool `form:\"active\"`\n\tMapExample  map[string]string\n\tNestedMap   map[string]map[string]string\n\tNestedArray [][]string\n}\n\n// use a single instance of Encoder, it caches struct info\nvar encoder *form.Encoder\n\nfunc main() {\n\tencoder = form.NewEncoder()\n\n\tuser := User{\n\t\tName:   \"joeybloggs\",\n\t\tAge:    3,\n\t\tGender: \"Male\",\n\t\tAddress: []Address{\n\t\t\t{Name: \"26 Here Blvd.\", Phone: \"9(999)999-9999\"},\n\t\t\t{Name: \"26 There Blvd.\", Phone: \"1(111)111-1111\"},\n\t\t},\n\t\tActive:      true,\n\t\tMapExample:  map[string]string{\"key\": \"value\"},\n\t\tNestedMap:   map[string]map[string]string{\"key\": {\"key\": \"value\"}},\n\t\tNestedArray: [][]string{{\"value\"}},\n\t}\n\n\t// must pass a pointer\n\tvalues, err := encoder.Encode(\u0026user)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfmt.Printf(\"%#v\\n\", values)\n}\n```\n\nRegistering Custom Types\n--------------\n\nDecoder\n```go\ndecoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {\n\treturn time.Parse(\"2006-01-02\", vals[0])\n}, time.Time{})\n```\nADDITIONAL: if a struct type is registered, the function will only be called if a url.Value exists for\nthe struct and not just the struct fields eg. url.Values{\"User\":\"Name%3Djoeybloggs\"} will call the\ncustom type function with 'User' as the type, however url.Values{\"User.Name\":\"joeybloggs\"} will not.\n\n\nEncoder\n```go\nencoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {\n\treturn []string{x.(time.Time).Format(\"2006-01-02\")}, nil\n}, time.Time{})\n```\n\nIgnoring Fields\n--------------\nyou can tell form to ignore fields using `-` in the tag\n```go\ntype MyStruct struct {\n\tField string `form:\"-\"`\n}\n```\n\nOmitempty\n--------------\nyou can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag\n```go\ntype MyStruct struct {\n\tField  string `form:\",omitempty\"`\n\tField2 string `form:\"CustomFieldName,omitempty\"`\n}\n```\n\nNotes\n------\nTo maximize compatibility with other systems the Encoder attempts\nto avoid using array indexes in url.Values if at all possible.\n\neg.\n```go\n// A struct field of\nField []string{\"1\", \"2\", \"3\"}\n\n// will be output a url.Value as\n\"Field\": []string{\"1\", \"2\", \"3\"}\n\nand not\n\"Field[0]\": []string{\"1\"}\n\"Field[1]\": []string{\"2\"}\n\"Field[2]\": []string{\"3\"}\n\n// however there are times where it is unavoidable, like with pointers\ni := int(1)\nField []*string{nil, nil, \u0026i}\n\n// to avoid index 1 and 2 must use index\n\"Field[2]\": []string{\"1\"}\n```\n\nBenchmarks\n------\n###### Run on M1 MacBook Pro using go version go1.20.6 darwin/amd64\n\nNOTE: the 1 allocation and B/op in the first 4 decodes is actually the struct allocating when passing it in, so primitives are actually zero allocation.\n\n```go\ngo test -run=NONE -bench=. -benchmem=true ./...\ngoos: darwin\ngoarch: arm64\npkg: github.com/go-playground/form/v4/benchmarks\nBenchmarkSimpleUserDecodeStruct-8                                8704111               121.1 ns/op            64 B/op          1 allocs/op\nBenchmarkSimpleUserDecodeStructParallel-8                       35916134                32.89 ns/op           64 B/op          1 allocs/op\nBenchmarkSimpleUserEncodeStruct-8                                3746173               320.7 ns/op           485 B/op         10 allocs/op\nBenchmarkSimpleUserEncodeStructParallel-8                        7293147               180.0 ns/op           485 B/op         10 allocs/op\nBenchmarkPrimitivesDecodeStructAllPrimitivesTypes-8              2993259               400.5 ns/op            96 B/op          1 allocs/op\nBenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-8     13023300                97.70 ns/op           96 B/op          1 allocs/op\nBenchmarkPrimitivesEncodeStructAllPrimitivesTypes-8               643202              1767 ns/op            2977 B/op         35 allocs/op\nBenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-8      1000000              1202 ns/op            2978 B/op         35 allocs/op\nBenchmarkComplexArrayDecodeStructAllTypes-8                       172630              6822 ns/op            2008 B/op        121 allocs/op\nBenchmarkComplexArrayDecodeStructAllTypesParallel-8               719788              1735 ns/op            2009 B/op        121 allocs/op\nBenchmarkComplexArrayEncodeStructAllTypes-8                       197052              5839 ns/op            7087 B/op        104 allocs/op\nBenchmarkComplexArrayEncodeStructAllTypesParallel-8               348039              3247 ns/op            7089 B/op        104 allocs/op\nBenchmarkComplexMapDecodeStructAllTypes-8                         139246              8550 ns/op            5313 B/op        130 allocs/op\nBenchmarkComplexMapDecodeStructAllTypesParallel-8                 409018              3143 ns/op            5317 B/op        130 allocs/op\nBenchmarkComplexMapEncodeStructAllTypes-8                         208833              5515 ns/op            4257 B/op        103 allocs/op\nBenchmarkComplexMapEncodeStructAllTypesParallel-8                 523833              2182 ns/op            4258 B/op        103 allocs/op\nBenchmarkDecodeNestedStruct-8                                     807690              1408 ns/op             344 B/op         14 allocs/op\nBenchmarkDecodeNestedStructParallel-8                            3409441               359.6 ns/op           344 B/op         14 allocs/op\nBenchmarkEncodeNestedStruct-8                                    1488520               803.6 ns/op           653 B/op         16 allocs/op\nBenchmarkEncodeNestedStructParallel-8                            3570204               346.6 ns/op           653 B/op         16 allocs/op\n```\n\nCompetitor benchmarks can be found [here](https://github.com/go-playground/form/blob/master/benchmarks/benchmarks.md)\n\nComplimentary Software\n----------------------\n\nHere is a list of software that compliments using this library post decoding.\n\n* [Validator](https://github.com/go-playground/validator) - Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving.\n* [mold](https://github.com/go-playground/mold) - Is a general library to help modify or set data within data structures and other objects.\n\nLicense\n------\nDistributed under MIT License, please see license file in code for more details.\n","funding_links":[],"categories":["表单","表單"],"sub_categories":["高级控制台界面","高級控制台界面"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGo-playground%2Fform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGo-playground%2Fform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGo-playground%2Fform/lists"}