diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index 02490646..ae0e8468 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -1,6 +1,7 @@ package mapping import ( + "encoding" "encoding/json" "errors" "fmt" @@ -318,6 +319,28 @@ func (u *Unmarshaler) processFieldStructWithMap(field reflect.StructField, value return nil } +func (u *Unmarshaler) processFieldTextUnmarshaler(field reflect.StructField, value reflect.Value, + mapValue interface{}) (bool, error) { + var tval encoding.TextUnmarshaler + var ok bool + + if field.Type.Kind() == reflect.Ptr { + tval, ok = value.Interface().(encoding.TextUnmarshaler) + } else { + tval, ok = value.Addr().Interface().(encoding.TextUnmarshaler) + } + if ok { + switch mv := mapValue.(type) { + case string: + return true, tval.UnmarshalText([]byte(mv)) + case []byte: + return true, tval.UnmarshalText(mv) + } + } + + return false, nil +} + func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect.Value, m Valuer, fullName string) error { key, opts, err := u.parseOptionsWithContext(field, m, fullName) @@ -348,8 +371,16 @@ func (u *Unmarshaler) processNamedFieldWithValue(field reflect.StructField, valu return fmt.Errorf("field %s mustn't be nil", key) } + if !value.CanSet() { + return fmt.Errorf("field %s is not settable", key) + } + maybeNewValue(field, value) + if yes, err := u.processFieldTextUnmarshaler(field, value, mapValue); yes { + return err + } + fieldKind := Deref(field.Type).Kind() switch fieldKind { case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct: diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index fe4c4375..97c27890 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/zeromicro/go-zero/core/stringx" ) @@ -537,10 +538,12 @@ func TestUnmarshalStringMapFromUnsupportedType(t *testing.T) { func TestUnmarshalStringMapFromNotSettableValue(t *testing.T) { var v struct { - sort map[string]string `key:"sort"` + sort map[string]string `key:"sort"` + psort *map[string]string `key:"sort"` } m := map[string]interface{}{ - "sort": `{"value":"ascend","emptyStr":""}`, + "sort": `{"value":"ascend","emptyStr":""}`, + "psort": `{"value":"ascend","emptyStr":""}`, } ast := assert.New(t) @@ -3018,6 +3021,24 @@ func TestUnmarshalJsonReaderArrayString(t *testing.T) { assert.NotNil(t, err) } +func TestGoogleUUID(t *testing.T) { + var val struct { + Uid uuid.UUID `json:"uid,optional"` + Uidp *uuid.UUID `json:"uidp,optional"` + } + assert.NoError(t, UnmarshalJsonBytes([]byte(`{ + "uid": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", + "uidp": "6ba7b810-9dad-11d1-80b4-00c04fd430c9"}`), &val)) + assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c8", val.Uid.String()) + assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c9", val.Uidp.String()) + assert.NoError(t, UnmarshalJsonMap(map[string]interface{}{ + "uid": []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c1"), + "uidp": []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c2"), + }, &val)) + assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c1", val.Uid.String()) + assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c2", val.Uidp.String()) +} + func BenchmarkDefaultValue(b *testing.B) { for i := 0; i < b.N; i++ { var a struct {