Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: form.Marshaler and form.Unmarshaler #63

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ It has the following features:

Common Questions

- Does it support encoding.TextUnmarshaler? No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable.
- Does it support encoding.TextUnmarshaler? No, instead we have `form.Unmarshaler` 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 )
Expand Down Expand Up @@ -230,6 +230,33 @@ encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
}, time.Time{})
```

Implementing Marshaler and Unmarshaler
--------------
Marshaler
```go
type CustomStruct struct {
A string
B string
}

func (c CustomStruct) MarshalForm() ([]string, error) {
return []string{ c.A, c.B }, nil
}
```

Unmarshaler
```go
type CustomStruct struct {
A string
B string
}

func (c *CustomStruct) UnmarshalForm(ss []string) error {
c.A = ss[0]
c.B = ss[1]
}
```

Ignoring Fields
--------------
you can tell form to ignore fields using `-` in the tag
Expand Down
51 changes: 45 additions & 6 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"time"
)

var unmarhsalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()

const (
errArraySize = "Array size of '%d' is larger than the maximum currently set on the decoder of '%d'. To increase this limit please see, SetMaxArraySize(size uint)"
errMissingStartBracket = "Invalid formatting for key '%s' missing '[' bracket"
Expand Down Expand Up @@ -63,7 +65,6 @@ func (d *decoder) parseMapData() {
}

for i = 0; i < len(k); i++ {

switch k[i] {
case '[':
idx = i
Expand Down Expand Up @@ -117,7 +118,6 @@ func (d *decoder) parseMapData() {

if ke.ivalue > rd.sliceLen {
rd.sliceLen = ke.ivalue

}
}

Expand All @@ -140,7 +140,6 @@ func (d *decoder) parseMapData() {
}

func (d *decoder) traverseStruct(v reflect.Value, typ reflect.Type, namespace []byte) (set bool) {

l := len(namespace)
first := l == 0

Expand Down Expand Up @@ -177,14 +176,12 @@ func (d *decoder) traverseStruct(v reflect.Value, typ reflect.Type, namespace []
}

func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx int) (set bool) {

var err error
v, kind := ExtractType(current)

arr, ok := d.values[string(namespace)]

if d.d.customTypeFuncs != nil {

if ok {
if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
val, err := cf(arr[idx:])
Expand All @@ -199,6 +196,14 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx in
}
}
}

if set, err = d.unmarshal(v, idx, arr); err != nil {
d.setError(namespace, err)
return
} else if set {
return
}

switch kind {
case reflect.Interface:
if !ok || idx == len(arr) {
Expand Down Expand Up @@ -602,7 +607,6 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx in
}

func (d *decoder) getMapKey(key string, current reflect.Value, namespace []byte) (err error) {

v, kind := ExtractType(current)

if d.d.customTypeFuncs != nil {
Expand Down Expand Up @@ -750,3 +754,38 @@ func (d *decoder) getMapKey(key string, current reflect.Value, namespace []byte)

return
}

func (d *decoder) unmarshal(v reflect.Value, idx int, arr []string) (bool, error) {
t := v.Type()
if t.Kind() != reflect.Ptr && v.CanAddr() {
v = v.Addr()
t = v.Type()
}
if v.Type().NumMethod() == 0 || !v.CanInterface() {
return false, nil
}

if !t.Implements(unmarhsalerType) {
return false, nil
}

if t.Kind() == reflect.Ptr && v.CanAddr() {
return d.unmarshalAddr(v, idx, arr)
}

um := v.Interface().(Unmarshaler)
if err := um.UnmarshalForm(arr[idx:]); err != nil {
return false, err
}
return true, nil
}

func (d *decoder) unmarshalAddr(v reflect.Value, idx int, arr []string) (bool, error) {
nv := reflect.New(v.Type().Elem())
um := nv.Interface().(Unmarshaler)
if err := um.UnmarshalForm(arr[idx:]); err != nil {
return false, err
}
v.Set(nv)
return true, nil
}
Loading
Loading