gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

doc.go (8139B)


      1 /*
      2 Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.
      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 eg. "Array[0]" and just "Array"
     10       with multiple values passed.
     11     - Slice honours the specified index. eg. if "Slice[2]" is the only Slice
     12       value passed down, it will be put at index 2; if slice isn't big enough
     13       it will be expanded.
     14     - Array honours the specified index. eg. if "Array[2]" is the only Array
     15       value passed down, it will be put at index 2; if array isn't big enough
     16       a warning will be printed and value ignored.
     17     - Only creates objects as necessary eg. if no `array` or `map` values are
     18       passed down, the `array` and `map` are left as their default values in
     19       the struct.
     20     - Allows for Custom Type registration.
     21     - Handles time.Time using RFC3339 time format by default,
     22       but can easily be changed by registering a Custom Type, see below.
     23     - Handles Encoding & Decoding of almost all Go types eg. can Decode into
     24       struct, array, map, int... and Encode a struct, array, map, int...
     25 
     26 Common Questions
     27 
     28 Questions
     29 
     30     Does it support encoding.TextUnmarshaler?
     31     No because TextUnmarshaler only accepts []byte but posted values can have
     32     multiple values, so is not suitable.
     33 
     34 	Mixing array/slice with array[idx]/slice[idx], in which order are they parsed?
     35 	array/slice then array[idx]/slice[idx]
     36 
     37 Supported Types
     38 
     39 out of the box supported types
     40 
     41     - string
     42     - bool
     43     - int, int8, int16, int32, int64
     44     - uint, uint8, uint16, uint32, uint64
     45     - float32, float64
     46     - struct and anonymous struct
     47     - interface{}
     48     - time.Time` - by default using RFC3339
     49     - a `pointer` to one of the above types
     50     - slice, array
     51     - map
     52     - `custom types` can override any of the above types
     53     - many other types may be supported inherently (eg. bson.ObjectId is
     54       type ObjectId string, which will get populated by the string type
     55 
     56     **NOTE**: map, struct and slice nesting are ad infinitum.
     57 
     58 Usage
     59 
     60 symbols
     61 
     62     - Use symbol `.` for separating fields/structs. (eg. `structfield.field`)
     63     - Use `[index or key]` for access to index of a slice/array or key for map.
     64       (eg. `arrayfield[0]`, `mapfield[keyvalue]`)
     65 
     66 html
     67 
     68     <form method="POST">
     69         <input type="text" name="Name" value="joeybloggs"/>
     70         <input type="text" name="Age" value="3"/>
     71         <input type="text" name="Gender" value="Male"/>
     72         <input type="text" name="Address[0].Name" value="26 Here Blvd."/>
     73         <input type="text" name="Address[0].Phone" value="9(999)999-9999"/>
     74         <input type="text" name="Address[1].Name" value="26 There Blvd."/>
     75         <input type="text" name="Address[1].Phone" value="1(111)111-1111"/>
     76         <input type="text" name="active" value="true"/>
     77         <input type="text" name="MapExample[key]" value="value"/>
     78         <input type="text" name="NestedMap[key][key]" value="value"/>
     79         <input type="text" name="NestedArray[0][0]" value="value"/>
     80         <input type="submit"/>
     81     </form>
     82 
     83 Example
     84 
     85 example decoding the above HTML
     86 
     87     package main
     88 
     89     import (
     90         "fmt"
     91         "log"
     92         "net/url"
     93 
     94         "github.com/go-playground/form/v4"
     95     )
     96 
     97     // Address contains address information
     98     type Address struct {
     99         Name  string
    100         Phone string
    101     }
    102 
    103     // User contains user information
    104     type User struct {
    105         Name        string
    106         Age         uint8
    107         Gender      string
    108         Address     []Address
    109         Active      bool `form:"active"`
    110         MapExample  map[string]string
    111         NestedMap   map[string]map[string]string
    112         NestedArray [][]string
    113     }
    114 
    115     // use a single instance of Decoder, it caches struct info
    116     var decoder *form.Decoder
    117 
    118     func main() {
    119         decoder = form.NewDecoder()
    120 
    121         // this simulates the results of http.Request's ParseForm() function
    122         values := parseForm()
    123 
    124         var user User
    125 
    126         // must pass a pointer
    127         err := decoder.Decode(&user, values)
    128         if err != nil {
    129             log.Panic(err)
    130         }
    131 
    132         fmt.Printf("%#v\n", user)
    133     }
    134 
    135     // this simulates the results of http.Request's ParseForm() function
    136     func parseForm() url.Values {
    137         return url.Values{
    138             "Name":                []string{"joeybloggs"},
    139             "Age":                 []string{"3"},
    140             "Gender":              []string{"Male"},
    141             "Address[0].Name":     []string{"26 Here Blvd."},
    142             "Address[0].Phone":    []string{"9(999)999-9999"},
    143             "Address[1].Name":     []string{"26 There Blvd."},
    144             "Address[1].Phone":    []string{"1(111)111-1111"},
    145             "active":              []string{"true"},
    146             "MapExample[key]":     []string{"value"},
    147             "NestedMap[key][key]": []string{"value"},
    148             "NestedArray[0][0]":   []string{"value"},
    149         }
    150     }
    151 
    152 example encoding
    153 
    154     package main
    155 
    156     import (
    157         "fmt"
    158         "log"
    159 
    160         "github.com/go-playground/form/v4"
    161     )
    162 
    163     // Address contains address information
    164     type Address struct {
    165         Name  string
    166         Phone string
    167     }
    168 
    169     // User contains user information
    170     type User struct {
    171         Name        string
    172         Age         uint8
    173         Gender      string
    174         Address     []Address
    175         Active      bool `form:"active"`
    176         MapExample  map[string]string
    177         NestedMap   map[string]map[string]string
    178         NestedArray [][]string
    179     }
    180 
    181     // use a single instance of Encoder, it caches struct info
    182     var encoder *form.Encoder
    183 
    184     func main() {
    185         encoder = form.NewEncoder()
    186 
    187         user := User{
    188             Name:   "joeybloggs",
    189             Age:    3,
    190             Gender: "Male",
    191             Address: []Address{
    192                 {Name: "26 Here Blvd.", Phone: "9(999)999-9999"},
    193                 {Name: "26 There Blvd.", Phone: "1(111)111-1111"},
    194             },
    195             Active:      true,
    196             MapExample:  map[string]string{"key": "value"},
    197             NestedMap:   map[string]map[string]string{"key": {"key": "value"}},
    198             NestedArray: [][]string{{"value"}},
    199         }
    200 
    201         // must pass a pointer
    202         values, err := encoder.Encode(&user)
    203         if err != nil {
    204             log.Panic(err)
    205         }
    206 
    207         fmt.Printf("%#v\n", values)
    208     }
    209 
    210 
    211 Registering Custom Types
    212 
    213 Decoder
    214 
    215     decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
    216             return time.Parse("2006-01-02", vals[0])
    217         }, time.Time{})
    218 
    219     ADDITIONAL: if a struct type is registered, the function will only be called
    220     if a url.Value exists for the struct and not just the struct fields
    221     eg. url.Values{"User":"Name%3Djoeybloggs"} will call the custom type function
    222     with 'User' as the type, however url.Values{"User.Name":"joeybloggs"} will not.
    223 
    224 Encoder
    225 
    226     encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
    227             return []string{x.(time.Time).Format("2006-01-02")}, nil
    228         }, time.Time{})
    229 
    230 
    231 Ignoring Fields
    232 
    233 you can tell form to ignore fields using `-` in the tag
    234 
    235     type MyStruct struct {
    236         Field string `form:"-"`
    237     }
    238 
    239 Omitempty
    240 
    241 you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag
    242 
    243     type MyStruct struct {
    244         Field  string `form:",omitempty"`
    245         Field2 string `form:"CustomFieldName,omitempty"`
    246     }
    247 
    248 
    249 Notes
    250 
    251 To maximize compatibility with other systems the Encoder attempts
    252 to avoid using array indexes in url.Values if at all possible.
    253 
    254     eg.
    255 
    256     // A struct field of
    257     Field []string{"1", "2", "3"}
    258 
    259     // will be output a url.Value as
    260     "Field": []string{"1", "2", "3"}
    261 
    262     and not
    263     "Field[0]": []string{"1"}
    264     "Field[1]": []string{"2"}
    265     "Field[2]": []string{"3"}
    266 
    267     // however there are times where it is unavoidable, like with pointers
    268     i := int(1)
    269     Field []*string{nil, nil, &i}
    270 
    271     // to avoid index 1 and 2 must use index
    272     "Field[2]": []string{"1"}
    273 
    274 */
    275 package form