mirror of
https://github.com/gin-gonic/gin.git
synced 2026-06-05 10:28:21 +08:00
Merge cb5463e5d88b42b17d233c7314daca4cbe0861b5 into ecd26c8835574b47acdc9cb6a5dddcc6fe922140
This commit is contained in:
commit
f2edee9781
@ -373,9 +373,9 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
|
|||||||
case multipart.FileHeader:
|
case multipart.FileHeader:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return json.API.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
return unmarshalFieldAsJSON(val, value)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
return json.API.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
return unmarshalFieldAsJSON(val, value)
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if !value.Elem().IsValid() {
|
if !value.Elem().IsValid() {
|
||||||
value.Set(reflect.New(value.Type().Elem()))
|
value.Set(reflect.New(value.Type().Elem()))
|
||||||
@ -548,3 +548,24 @@ func setFormMap(ptr any, form map[string][]string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unmarshalFieldAsJSON tries to unmarshal a form value as JSON. If the initial
|
||||||
|
// unmarshal fails, it retries by wrapping the value in double quotes, treating
|
||||||
|
// the form value as a JSON string. This is necessary because form values are
|
||||||
|
// plain strings (e.g. "2020/09/23 13:20:49") while json.Unmarshal expects
|
||||||
|
// valid JSON input (e.g. quoted strings for string-like values).
|
||||||
|
func unmarshalFieldAsJSON(val string, value reflect.Value) error {
|
||||||
|
b := bytesconv.StringToBytes(val)
|
||||||
|
if err := json.API.Unmarshal(b, value.Addr().Interface()); err != nil {
|
||||||
|
// The raw form value is not valid JSON. Wrap it in quotes so that
|
||||||
|
// types implementing json.Unmarshaler that expect a JSON string
|
||||||
|
// (e.g. custom datetime types) can decode it successfully.
|
||||||
|
quoted := `"` + val + `"`
|
||||||
|
if errRetry := json.API.Unmarshal(bytesconv.StringToBytes(quoted), value.Addr().Interface()); errRetry != nil {
|
||||||
|
// Return the original error — the quoted attempt was only a
|
||||||
|
// best-effort fallback.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ package binding
|
|||||||
import (
|
import (
|
||||||
"encoding"
|
"encoding"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -484,6 +485,50 @@ func TestMappingMapField(t *testing.T) {
|
|||||||
assert.Equal(t, map[string]int{"one": 1}, s.M)
|
assert.Equal(t, map[string]int{"one": 1}, s.M)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// customDateTime is a custom type that implements json.Unmarshaler.
|
||||||
|
// It expects a JSON-quoted string in "2006/01/02 15:04:05" format.
|
||||||
|
type customDateTime time.Time
|
||||||
|
|
||||||
|
const customDateTimeFormat = "2006/01/02 15:04:05"
|
||||||
|
|
||||||
|
func (t *customDateTime) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parsed, err := time.Parse(customDateTimeFormat, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = customDateTime(parsed)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingCustomJSONUnmarshalerStructFromForm(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Time customDateTime `form:"Time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mappingByPtr(&s, formSource{"Time": {"2020/09/23 13:20:49"}}, "form")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected, _ := time.Parse(customDateTimeFormat, "2020/09/23 13:20:49")
|
||||||
|
assert.Equal(t, customDateTime(expected), s.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingCustomJSONUnmarshalerStructValidJSON(t *testing.T) {
|
||||||
|
// When the form value is already valid JSON, it should still work.
|
||||||
|
var s struct {
|
||||||
|
J struct {
|
||||||
|
I int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mappingByPtr(&s, formSource{"J": {`{"I": 9}`}}, "form")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 9, s.J.I)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMappingIgnoredCircularRef(t *testing.T) {
|
func TestMappingIgnoredCircularRef(t *testing.T) {
|
||||||
type S struct {
|
type S struct {
|
||||||
S *S `form:"-"`
|
S *S `form:"-"`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user