Skip to content

Commit bee67eb

Browse files
author
Dean Karn
committed
Add omitempty support
1 parent 920e9aa commit bee67eb

File tree

9 files changed

+203
-73
lines changed

9 files changed

+203
-73
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Supported Types ( out of the box )
3939
* `slice`, `array`
4040
* `map`
4141
* `custom types` can override any of the above types
42-
* many other types may be supported inherently (eg. `bson.ObjectId` is `type ObjectId string`, which will get populated by the string type
42+
* many other types may be supported inherently
4343

4444
**NOTE**: `map`, `struct` and `slice` nesting are ad infinitum.
4545

@@ -237,6 +237,16 @@ type MyStruct struct {
237237
}
238238
```
239239

240+
Omitempty
241+
--------------
242+
you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag
243+
```go
244+
type MyStruct struct {
245+
Field string `form:",omitempty"`
246+
Field2 string `form:"CustomFieldName,omitempty"`
247+
}
248+
```
249+
240250
Notes
241251
------
242252
To maximize compatibility with other systems the Encoder attempts
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/go-playground/form"
8+
)
9+
10+
// Test ...
11+
type Test struct {
12+
String string `form:",omitempty"`
13+
Array []string
14+
Map map[string]string
15+
}
16+
17+
// use a single instance of Encoder, it caches struct info
18+
var encoder *form.Encoder
19+
20+
func main() {
21+
var t Test
22+
23+
encoder = form.NewEncoder()
24+
25+
values, err := encoder.Encode(t)
26+
if err != nil {
27+
log.Panic(err)
28+
}
29+
30+
fmt.Printf("%#v\n", values)
31+
32+
t.String = "String Val"
33+
t.Array = []string{"arr1"}
34+
35+
values, err = encoder.Encode(t)
36+
if err != nil {
37+
log.Panic(err)
38+
}
39+
40+
fmt.Printf("%#v\n", values)
41+
}

benchmarks/benchmarks.md

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,56 @@
11
## Benchmarks
22

3-
Competitor Benchmarks Last Run Feb 15, 2017
3+
All Benchmarks Last Run July 30, 2017
44

5-
Run on i5-7600 16 GB DDR4-2400 using Go version go1.7.5 linux/amd64
5+
Run on Dell XPS 15 i7-7700HQ 32GB using Go version go1.8.3 linux/amd64
6+
go test -run=NONE -bench=. -benchmem=true
67

78
### go-playground/form
89
```go
9-
PASS
10-
BenchmarkSimpleUserDecodeStruct-4 5000000 252 ns/op 64 B/op 1 allocs/op
11-
BenchmarkSimpleUserDecodeStructParallel-4 20000000 74.1 ns/op 64 B/op 1 allocs/op
12-
BenchmarkSimpleUserEncodeStruct-4 2000000 800 ns/op 485 B/op 11 allocs/op
13-
BenchmarkSimpleUserEncodeStructParallel-4 10000000 220 ns/op 485 B/op 11 allocs/op
14-
BenchmarkPrimitivesDecodeStructAllPrimitivesTypes-4 2000000 773 ns/op 96 B/op 1 allocs/op
15-
BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-4 10000000 225 ns/op 96 B/op 1 allocs/op
16-
BenchmarkPrimitivesEncodeStructAllPrimitivesTypes-4 300000 4330 ns/op 3009 B/op 46 allocs/op
17-
BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-4 1000000 1131 ns/op 3009 B/op 46 allocs/op
18-
BenchmarkComplexArrayDecodeStructAllTypes-4 100000 13316 ns/op 2256 B/op 121 allocs/op
19-
BenchmarkComplexArrayDecodeStructAllTypesParallel-4 300000 3980 ns/op 2256 B/op 121 allocs/op
20-
BenchmarkComplexArrayEncodeStructAllTypes-4 100000 14038 ns/op 7287 B/op 146 allocs/op
21-
BenchmarkComplexArrayEncodeStructAllTypesParallel-4 300000 3646 ns/op 7288 B/op 146 allocs/op
22-
BenchmarkComplexMapDecodeStructAllTypes-4 100000 18042 ns/op 5305 B/op 130 allocs/op
23-
BenchmarkComplexMapDecodeStructAllTypesParallel-4 300000 5051 ns/op 5306 B/op 130 allocs/op
24-
BenchmarkComplexMapEncodeStructAllTypes-4 100000 14177 ns/op 7098 B/op 175 allocs/op
25-
BenchmarkComplexMapEncodeStructAllTypesParallel-4 300000 3693 ns/op 7098 B/op 175 allocs/op
26-
BenchmarkDecodeNestedStruct-4 500000 2762 ns/op 384 B/op 14 allocs/op
27-
BenchmarkDecodeNestedStructParallel-4 2000000 785 ns/op 384 B/op 14 allocs/op
28-
BenchmarkEncodeNestedStruct-4 1000000 1779 ns/op 704 B/op 16 allocs/op
29-
BenchmarkEncodeNestedStructParallel-4 3000000 493 ns/op 704 B/op 16 allocs/op
10+
BenchmarkSimpleUserDecodeStruct-8 5000000 264 ns/op 64 B/op 1 allocs/op
11+
BenchmarkSimpleUserDecodeStructParallel-8 20000000 80.7 ns/op 64 B/op 1 allocs/op
12+
BenchmarkSimpleUserEncodeStruct-8 1000000 1097 ns/op 485 B/op 11 allocs/op
13+
BenchmarkSimpleUserEncodeStructParallel-8 5000000 239 ns/op 485 B/op 11 allocs/op
14+
BenchmarkPrimitivesDecodeStructAllPrimitivesTypes-8 2000000 799 ns/op 96 B/op 1 allocs/op
15+
BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-8 5000000 237 ns/op 96 B/op 1 allocs/op
16+
BenchmarkPrimitivesEncodeStructAllPrimitivesTypes-8 300000 6672 ns/op 3010 B/op 46 allocs/op
17+
BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-8 1000000 1207 ns/op 3010 B/op 46 allocs/op
18+
BenchmarkComplexArrayDecodeStructAllTypes-8 100000 14374 ns/op 2249 B/op 121 allocs/op
19+
BenchmarkComplexArrayDecodeStructAllTypesParallel-8 300000 3867 ns/op 2249 B/op 121 allocs/op
20+
BenchmarkComplexArrayEncodeStructAllTypes-8 100000 20531 ns/op 7161 B/op 146 allocs/op
21+
BenchmarkComplexArrayEncodeStructAllTypesParallel-8 300000 3839 ns/op 7162 B/op 146 allocs/op
22+
BenchmarkComplexMapDecodeStructAllTypes-8 100000 20745 ns/op 5306 B/op 130 allocs/op
23+
BenchmarkComplexMapDecodeStructAllTypesParallel-8 300000 5164 ns/op 5312 B/op 130 allocs/op
24+
BenchmarkComplexMapEncodeStructAllTypes-8 100000 18658 ns/op 7066 B/op 175 allocs/op
25+
BenchmarkComplexMapEncodeStructAllTypesParallel-8 300000 4019 ns/op 7068 B/op 175 allocs/op
26+
BenchmarkDecodeNestedStruct-8 500000 3039 ns/op 384 B/op 14 allocs/op
27+
BenchmarkDecodeNestedStructParallel-8 2000000 832 ns/op 384 B/op 14 allocs/op
28+
BenchmarkEncodeNestedStruct-8 1000000 2005 ns/op 693 B/op 16 allocs/op
29+
BenchmarkEncodeNestedStructParallel-8 3000000 534 ns/op 693 B/op 16 allocs/op
3030
```
3131

3232
### gorilla/schema
3333
```go
34-
BenchmarkSimpleUserStructGorilla-4 1000000 2077 ns/op 520 B/op 23 allocs/op
35-
BenchmarkSimpleUserStructGorillaParallel-4 2000000 667 ns/op 520 B/op 23 allocs/op
36-
BenchmarkPrimitivesStructAllPrimitivesTypesGorilla-4 200000 7997 ns/op 1536 B/op 84 allocs/op
37-
BenchmarkPrimitivesStructAllPrimitivesTypesGorillaParallel-4 500000 2429 ns/op 1536 B/op 84 allocs/op
38-
BenchmarkComplexArrayStructAllTypesGorilla-4 50000 23286 ns/op 5416 B/op 223 allocs/op
39-
BenchmarkComplexArrayStructAllTypesGorillaParallel-4 200000 6773 ns/op 5416 B/op 223 allocs/op
40-
BenchmarkArrayMapNestedStructGorilla-4 200000 7745 ns/op 2285 B/op 75 allocs/op
41-
BenchmarkArrayMapNestedStructGorillaParallel-4 500000 2421 ns/op 2285 B/op 75 allocs/op
34+
BenchmarkSimpleUserStructGorilla-8 500000 2968 ns/op 568 B/op 27 allocs/op
35+
BenchmarkSimpleUserStructGorillaParallel-8 2000000 798 ns/op 568 B/op 27 allocs/op
36+
BenchmarkPrimitivesStructAllPrimitivesTypesGorilla-8 200000 10666 ns/op 1616 B/op 98 allocs/op
37+
BenchmarkPrimitivesStructAllPrimitivesTypesGorillaParallel-8 500000 2814 ns/op 1616 B/op 98 allocs/op
38+
BenchmarkComplexArrayStructAllTypesGorilla-8 50000 29731 ns/op 5528 B/op 240 allocs/op
39+
BenchmarkComplexArrayStructAllTypesGorillaParallel-8 200000 7657 ns/op 5528 B/op 240 allocs/op
40+
BenchmarkArrayMapNestedStructGorilla-8 200000 9546 ns/op 2397 B/op 82 allocs/op
41+
BenchmarkArrayMapNestedStructGorillaParallel-8 500000 2623 ns/op 2397 B/op 82 allocs/op
4242
```
4343

4444
### monoculum/formam
4545
```go
46-
BenchmarkSimpleUserStructFormam-4 1000000 1927 ns/op 232 B/op 16 allocs/op
47-
BenchmarkSimpleUserStructFormamParallel-4 3000000 514 ns/op 232 B/op 16 allocs/op
48-
BenchmarkPrimitivesStructAllPrimitivesFormamTypes-4 200000 7135 ns/op 1088 B/op 121 allocs/op
49-
BenchmarkPrimitivesStructAllPrimitivesTypesFormamParallel-4 1000000 1870 ns/op 1088 B/op 121 allocs/op
50-
BenchmarkComplexArrayStructAllTypesFormam-4 50000 30957 ns/op 5608 B/op 485 allocs/op
51-
BenchmarkComplexArrayStructAllTypesFormamParallel-4 200000 8848 ns/op 5594 B/op 484 allocs/op
52-
BenchmarkComplexMapStructAllTypesFormam-4 50000 37984 ns/op 14647 B/op 534 allocs/op
53-
BenchmarkComplexMapStructAllTypesFormamParallel-4 200000 9795 ns/op 14658 B/op 534 allocs/op
46+
BenchmarkSimpleUserStructFormam-8 500000 2888 ns/op 232 B/op 16 allocs/op
47+
BenchmarkSimpleUserStructFormamParallel-8 2000000 766 ns/op 232 B/op 16 allocs/op
48+
BenchmarkPrimitivesStructAllPrimitivesFormamTypes-8 200000 8179 ns/op 1088 B/op 121 allocs/op
49+
BenchmarkPrimitivesStructAllPrimitivesTypesFormamParallel-8 1000000 2235 ns/op 1088 B/op 121 allocs/op
50+
BenchmarkComplexArrayStructAllTypesFormam-8 50000 36620 ns/op 5561 B/op 482 allocs/op
51+
BenchmarkComplexArrayStructAllTypesFormamParallel-8 200000 9460 ns/op 5560 B/op 482 allocs/op
52+
BenchmarkComplexMapStructAllTypesFormam-8 30000 43515 ns/op 14649 B/op 534 allocs/op
53+
BenchmarkComplexMapStructAllTypesFormamParallel-8 200000 10842 ns/op 14652 B/op 534 allocs/op
5454
--- FAIL: BenchmarkArrayMapNestedStructFormam
5555
formam_test.go:137: formam: not supported type for field "Value" in path "NestedPtrArray[0].Value". Maybe you should to include it the UnmarshalText interface or register it using custom type?
5656
--- FAIL: BenchmarkArrayMapNestedStructFormamParallel
@@ -59,28 +59,28 @@ BenchmarkComplexMapStructAllTypesFormamParallel-4 200000
5959

6060
### ajg/form
6161
```go
62-
BenchmarkSimpleUserDecodeStructAGJForm-4 300000 5150 ns/op 1336 B/op 42 allocs/op
63-
BenchmarkSimpleUserDecodeStructParallelAGJFrom-4 1000000 1372 ns/op 1336 B/op 42 allocs/op
64-
BenchmarkSimpleUserEncodeStructAGJForm-4 300000 4196 ns/op 1304 B/op 37 allocs/op
65-
BenchmarkSimpleUserEncodeStructParallelAGJForm-4 1000000 1129 ns/op 1304 B/op 37 allocs/op
66-
BenchmarkPrimitivesDecodeStructAllPrimitivesTypesAGJForm-4 100000 18605 ns/op 5718 B/op 171 allocs/op
67-
BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallelAGJForm-4 300000 4941 ns/op 5718 B/op 171 allocs/op
68-
BenchmarkPrimitivesEncodeStructAllPrimitivesTypesAGJForm-4 100000 14417 ns/op 5903 B/op 110 allocs/op
69-
BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallelAGJForm-4 500000 3702 ns/op 5904 B/op 110 allocs/op
62+
BenchmarkSimpleUserDecodeStructAGJForm-8 300000 5567 ns/op 1320 B/op 34 allocs/op
63+
BenchmarkSimpleUserDecodeStructParallelAGJFrom-8 1000000 1482 ns/op 1320 B/op 34 allocs/op
64+
BenchmarkSimpleUserEncodeStructAGJForm-8 300000 4699 ns/op 1272 B/op 29 allocs/op
65+
BenchmarkSimpleUserEncodeStructParallelAGJForm-8 1000000 1239 ns/op 1272 B/op 29 allocs/op
66+
BenchmarkPrimitivesDecodeStructAllPrimitivesTypesAGJForm-8 100000 20476 ns/op 5662 B/op 143 allocs/op
67+
BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallelAGJForm-8 300000 5039 ns/op 5662 B/op 143 allocs/op
68+
BenchmarkPrimitivesEncodeStructAllPrimitivesTypesAGJForm-8 100000 15661 ns/op 5792 B/op 82 allocs/op
69+
BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallelAGJForm-8 300000 3851 ns/op 5792 B/op 82 allocs/op
7070
--- FAIL: BenchmarkComplexArrayDecodeStructAllTypesAGJForm
71-
ajg_form_test.go:127: is not a valid index for type []uint32
71+
ajg_form_test.go:127: is not a valid index for type []int32
7272
--- FAIL: BenchmarkComplexArrayDecodeStructAllTypesParallelAGJForm
73-
ajg_form_test.go:140: Int8Ptr[1] doesn't exist in benchmarks.ComplexArrayStruct
74-
BenchmarkComplexArrayEncodeStructAllTypesAGJForm-4 30000 61548 ns/op 22195 B/op 538 allocs/op
75-
BenchmarkComplexArrayEncodeStructAllTypesParallelAGJForm-4 100000 16386 ns/op 22193 B/op 538 allocs/op
76-
BenchmarkComplexMapDecodeStructAllTypesAGJForm-4 20000 76838 ns/op 22498 B/op 692 allocs/op
77-
BenchmarkComplexMapDecodeStructAllTypesParallelAGJForm-4 100000 20941 ns/op 22496 B/op 692 allocs/op
78-
BenchmarkComplexMapEncodeStructAllTypesAGJForm-4 50000 40160 ns/op 18286 B/op 419 allocs/op
79-
BenchmarkComplexMapEncodeStructAllTypesParallelAGJForm-4 200000 10761 ns/op 18289 B/op 419 allocs/op
73+
ajg_form_test.go:140: NestedInt[0][0] doesn't exist in benchmarks.ComplexArrayStruct
74+
BenchmarkComplexArrayEncodeStructAllTypesAGJForm-8 20000 67813 ns/op 21553 B/op 400 allocs/op
75+
BenchmarkComplexArrayEncodeStructAllTypesParallelAGJForm-8 100000 17175 ns/op 21552 B/op 400 allocs/op
76+
BenchmarkComplexMapDecodeStructAllTypesAGJForm-8 20000 84436 ns/op 22294 B/op 592 allocs/op
77+
BenchmarkComplexMapDecodeStructAllTypesParallelAGJForm-8 100000 21599 ns/op 22298 B/op 592 allocs/op
78+
BenchmarkComplexMapEncodeStructAllTypesAGJForm-8 30000 49011 ns/op 17958 B/op 323 allocs/op
79+
BenchmarkComplexMapEncodeStructAllTypesParallelAGJForm-8 200000 11763 ns/op 17958 B/op 323 allocs/op
8080
--- FAIL: BenchmarkDecodeNestedStructAGJForm
8181
ajg_form_test.go:261: NestedArray[0] doesn't exist in benchmarks.NestedStruct
8282
--- FAIL: BenchmarkDecodeNestedStructParallelAGJForm
8383
ajg_form_test.go:275: NestedArray[0] doesn't exist in benchmarks.NestedStruct
84-
BenchmarkEncodeNestedStructAGJForm-4 100000 15989 ns/op 5838 B/op 147 allocs/op
85-
BenchmarkEncodeNestedStructParallelAGJForm-4 300000 4272 ns/op 5838 B/op 147 allocs/op
84+
BenchmarkEncodeNestedStructAGJForm-8 100000 18583 ns/op 5704 B/op 113 allocs/op
85+
BenchmarkEncodeNestedStructParallelAGJForm-8 300000 4683 ns/op 5704 B/op 113 allocs/op
8686
```

cache.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type cachedField struct {
2626
idx int
2727
name string
2828
isAnonymous bool
29+
isOmitEmpty bool
2930
}
3031

3132
type cachedStruct struct {
@@ -85,12 +86,13 @@ func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key refle
8586

8687
var fld reflect.StructField
8788
var name string
89+
var idx int
90+
var isOmitEmpty bool
8891

8992
for i := 0; i < numFields; i++ {
90-
93+
isOmitEmpty = false
9194
fld = typ.Field(i)
9295

93-
// fmt.Println("PkgPath:", fld.PkgPath, " Anonymous:", fld.Anonymous, " Name:", fld.Name)
9496
if fld.PkgPath != blank && !fld.Anonymous {
9597
continue
9698
}
@@ -99,13 +101,8 @@ func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key refle
99101
name = s.tagFn(fld)
100102
} else {
101103
name = fld.Tag.Get(tagName)
102-
103-
if commaIndex := strings.Index(name, ","); commaIndex != -1 {
104-
name = name[:commaIndex]
105-
}
106104
}
107105

108-
// fmt.Println("Ignore:", name == ignore)
109106
if name == ignore {
110107
continue
111108
}
@@ -114,11 +111,17 @@ func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key refle
114111
continue
115112
}
116113

114+
// check for omitempty
115+
if idx = strings.LastIndexByte(name, ','); idx != -1 {
116+
isOmitEmpty = name[idx+1:] == "omitempty"
117+
name = name[:idx]
118+
}
119+
117120
if len(name) == 0 {
118121
name = fld.Name
119122
}
120123

121-
cs.fields = append(cs.fields, cachedField{idx: i, name: name, isAnonymous: fld.Anonymous})
124+
cs.fields = append(cs.fields, cachedField{idx: i, name: name, isAnonymous: fld.Anonymous, isOmitEmpty: isOmitEmpty})
122125
}
123126

124127
sort.Sort(cs.fields)

doc.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@ you can tell form to ignore fields using `-` in the tag
230230
Field string `form:"-"`
231231
}
232232
233+
Omitempty
234+
235+
you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag
236+
237+
type MyStruct struct {
238+
Field string `form:",omitempty"`
239+
Field2 string `form:"CustomFieldName,omitempty"`
240+
}
241+
242+
233243
Notes
234244
235245
To maximize compatibility with other systems the Encoder attempts

0 commit comments

Comments
 (0)