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
27 changes: 21 additions & 6 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ func (d *decoder) parseMapData() {
}

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

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

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

}
}

Expand All @@ -140,7 +138,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 +174,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 +194,27 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx in
}
}
}
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the extra scope is needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added so that v could be shadowed and modified without changing the code later on, but I'll just assign all modifications to a new var instead.

v := v // deliberately shadow v as to not modify
t := v.Type()
if t.Kind() != reflect.Ptr && v.CanAddr() {
v = v.Addr()
}
if um, ok := v.Interface().(Unmarshaler); ok {
// if receiver is a nil pointer, set before calling function.
if t.Kind() == reflect.Ptr && v.IsNil() {
t = t.Elem()
v.Set(reflect.New(t))
um = v.Interface().(Unmarshaler)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to avoid doing this/duplicating the Ptr logic of creating the new element?

below this same logic exists for Ptr already https://github.com/go-playground/form/pull/63/files#diff-2a967f84f1af0d52111b8dbfbeadc44bfeb1092dacebb2bc0648a0e57284c04bR226-R230.

It would be great if it's possible to keep the flow of the program using that same logic.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a look at this and see if anything can be done

if err = um.UnmarshalForm(arr[idx:]); err != nil {
d.setError(namespace, err)
return
}
set = true
return
}
}
switch kind {
case reflect.Interface:
if !ok || idx == len(arr) {
Expand Down Expand Up @@ -602,7 +618,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
Loading
Loading