All Projects → go-playground → Form

go-playground / Form

Licence: mit
🚂 Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.

Programming Languages

go
31211 projects - #10 most used programming language

Projects that are alternatives of or similar to Form

Ikigajson
A high performance JSON library in Swift
Stars: ✭ 302 (-33.48%)
Mutual labels:  decoding, encoder
Formam
a package for decode form's values into struct in Go
Stars: ✭ 160 (-64.76%)
Mutual labels:  decoding, form
Qs
Go module for encoding structs into URL query parameters
Stars: ✭ 55 (-87.89%)
Mutual labels:  encoder, form
Enmime
MIME mail encoding and decoding package for Go
Stars: ✭ 246 (-45.81%)
Mutual labels:  parser, encoder
pycayennelpp
A Cayenne Low Power Payload (CayenneLPP) decoder and encoder for Python
Stars: ✭ 17 (-96.26%)
Mutual labels:  encoder, decoding
sms
A Go library for encoding and decoding SMSs
Stars: ✭ 37 (-91.85%)
Mutual labels:  encoder, decoding
X509
A PHP library for X.509 public key certificates, attribute certificates, certification requests and certification path validation.
Stars: ✭ 27 (-94.05%)
Mutual labels:  parser, decoding
AnimatedGif
📼 A high performance .NET library for reading and creating animated GIFs
Stars: ✭ 106 (-76.65%)
Mutual labels:  encoder, decoding
Json Rust
JSON implementation in Rust
Stars: ✭ 395 (-13%)
Mutual labels:  parser, encoder
Crossplane
Quick and reliable way to convert NGINX configurations into JSON and back.
Stars: ✭ 407 (-10.35%)
Mutual labels:  parser
Mini Player
Stars: ✭ 435 (-4.19%)
Mutual labels:  form
Tomlplusplus
Header-only TOML config file parser and serializer for C++17 (and later!).
Stars: ✭ 403 (-11.23%)
Mutual labels:  parser
Javalang
Pure Python Java parser and tools
Stars: ✭ 408 (-10.13%)
Mutual labels:  parser
Picofeed
PHP library to parse and write RSS/Atom feeds
Stars: ✭ 439 (-3.3%)
Mutual labels:  parser
Jsonparser
One of the fastest alternative JSON parser for Go that does not require schema
Stars: ✭ 4,323 (+852.2%)
Mutual labels:  parser
Fasthan
fastHan是基于fastNLP与pytorch实现的中文自然语言处理工具,像spacy一样调用方便。
Stars: ✭ 449 (-1.1%)
Mutual labels:  parser
Php Parser
🌿 NodeJS PHP Parser - extract AST or tokens (PHP5 and PHP7)
Stars: ✭ 400 (-11.89%)
Mutual labels:  parser
Datefinder
Find dates inside text using Python and get back datetime objects
Stars: ✭ 397 (-12.56%)
Mutual labels:  parser
Exifr
📷 The fastest and most versatile JS EXIF reading library.
Stars: ✭ 448 (-1.32%)
Mutual labels:  parser
H265ize
A node utility utilizing ffmpeg to encode videos with the hevc codec.
Stars: ✭ 443 (-2.42%)
Mutual labels:  encoder

Package form

Project status Build Status Coverage Status Go Report Card GoDoc License Gitter

Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.

It has the following features:

  • Supports map of almost all types.
  • Supports both Numbered and Normal arrays eg. "Array[0]" and just "Array" with multiple values passed.
  • 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.
  • 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.
  • 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.
  • Allows for Custom Type registration.
  • Handles time.Time using RFC3339 time format by default, but can easily be changed by registering a Custom Type, see below.
  • Handles Encoding & Decoding of almost all Go types eg. can Decode into struct, array, map, int... and Encode a struct, array, map, int...

Common Questions

  • Does it support encoding.TextUnmarshaler? No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable.
  • Mixing array/slice with array[idx]/slice[idx], in which order are they parsed? array/slice then array[idx]/slice[idx]

Supported Types ( out of the box )

  • string
  • bool
  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64
  • float32, float64
  • struct and anonymous struct
  • interface{}
  • time.Time - by default using RFC3339
  • a pointer to one of the above types
  • slice, array
  • map
  • custom types can override any of the above types
  • many other types may be supported inherently

NOTE: map, struct and slice nesting are ad infinitum.

Installation

Use go get.

go get github.com/go-playground/form

Then import the form package into your own code.

import "github.com/go-playground/form/v4"

Usage

  • Use symbol . for separating fields/structs. (eg. structfield.field)
  • Use [index or key] for access to index of a slice/array or key for map. (eg. 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>

Examples

Decoding

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
	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
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"},
	}
}

Encoding

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
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)
}

Registering Custom Types

Decoder

decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
	return time.Parse("2006-01-02", vals[0])
}, time.Time{})

ADDITIONAL: if a struct type is registered, the function will only be called if a url.Value exists for the struct and not just the struct fields eg. url.Values{"User":"Name%3Djoeybloggs"} will call the custom type function with 'User' as the type, however url.Values{"User.Name":"joeybloggs"} will not.

Encoder

encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
	return []string{x.(time.Time).Format("2006-01-02")}, nil
}, time.Time{})

Ignoring Fields

you can tell form to ignore fields using - in the tag

type MyStruct struct {
	Field string `form:"-"`
}

Omitempty

you can tell form to omit empty fields using ,omitempty or FieldName,omitempty in the tag

type MyStruct struct {
	Field  string `form:",omitempty"`
	Field2 string `form:"CustomFieldName,omitempty"`
}

Notes

To maximize compatibility with other systems the Encoder attempts to avoid using array indexes in url.Values if at all possible.

eg.

// 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"}

Benchmarks

Run on MacBook Pro (15-inch, 2017) using go version go1.10.1 darwin/amd64

NOTE: 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.

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

Competitor benchmarks can be found here

Complimentary Software

Here is a list of software that compliments using this library post decoding.

  • Validator - Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving.
  • mold - Is a general library to help modify or set data within data structures and other objects.

Package Versioning

I'm jumping on the vendoring bandwagon, you should vendor this package as I will not be creating different version with gopkg.in like allot of my other libraries.

Why? because my time is spread pretty thin maintaining all of the libraries I have + LIFE, it is so freeing not to worry about it and will help me keep pouring out bigger and better things for you the community.

License

Distributed under MIT License, please see license file in code for more details.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].