From 461274666314f6adeaa48a190510742f6edb34a2 Mon Sep 17 00:00:00 2001 From: hz Date: Fri, 1 Nov 2024 10:40:50 +0800 Subject: [PATCH] improved toExportedName and trySolveTargetName --- ctrl_test.go | 78 +++++++++++++++++++++++----------------------- cvts.go | 88 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 111 insertions(+), 55 deletions(-) diff --git a/ctrl_test.go b/ctrl_test.go index e16e496..70254c5 100644 --- a/ctrl_test.go +++ b/ctrl_test.go @@ -1353,30 +1353,6 @@ func TestMapToString(t *testing.T) { //nolint:revive // tm2 := time.Date(2003, 9, 1, 23, 59, 59, 3579, timeZone) // tm3 := time.Date(2015, 1, 29, 19, 31, 37, 77, timeZone2) - expect2 := evendeep.User{ - Name: "Bob", - Birthday: &tm, - Age: 24, - EmployeID: 7, - Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", - Image: []byte{95, 27, 43, 66, 0, 21, 210}, - Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, - Valid: true, - } - - expect3 := evendeep.Employee2{ - Base: evendeep.Base{ - Name: "Bob", - Birthday: &tm, - Age: 24, - EmployeID: 7, - }, - Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", - Image: []byte{95, 27, 43, 66, 0, 21, 210}, - Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, - Valid: true, - } - var s2 evendeep.User var s3 evendeep.Employee2 var str1 string @@ -1411,20 +1387,31 @@ func TestMapToString(t *testing.T) { //nolint:revive "Valid": true }` - cases := []evendeep.TestCase{ - evendeep.NewTestCase( - "map -> string [json]", - map1, &str1, &expect1, - []evendeep.Opt{ - evendeep.WithStringMarshaller(func(v interface{}) ([]byte, error) { //nolint:revive - return json.MarshalIndent(v, "", " ") - }), - evendeep.WithMergeStrategyOpt, - evendeep.WithAutoExpandStructOpt, - }, - nil, - ), + expect2 := evendeep.User{ + Name: "Bob", + Birthday: &tm, + Age: 24, + EmployeID: 7, + Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", + Image: []byte{95, 27, 43, 66, 0, 21, 210}, + Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, + Valid: true, + } + expect3 := evendeep.Employee2{ + Base: evendeep.Base{ + Name: "Bob", + Birthday: &tm, + Age: 24, + EmployeID: 7, + }, + Avatar: "https://tse4-mm.cn.bing.net/th/id/OIP-C.SAy__OKoxrIqrXWAb7Tj1wHaEC?pid=ImgDet&rs=1", + Image: []byte{95, 27, 43, 66, 0, 21, 210}, + Attr: &evendeep.Attr{Attrs: []string{helloString, worldString}}, + Valid: true, + } + + cases := []evendeep.TestCase{ evendeep.NewTestCase( "map -> struct User", map1, &s2, &expect2, @@ -1437,12 +1424,27 @@ func TestMapToString(t *testing.T) { //nolint:revive []evendeep.Opt{evendeep.WithMergeStrategyOpt, evendeep.WithAutoExpandStructOpt}, nil, ), + evendeep.NewTestCase( + "map -> string [json]", + map1, &str1, &expect1, + []evendeep.Opt{ + evendeep.WithStringMarshaller(func(v interface{}) ([]byte, error) { //nolint:revive + return json.MarshalIndent(v, "", " ") + }), + evendeep.WithMergeStrategyOpt, + evendeep.WithAutoExpandStructOpt, + }, + nil, + ), } for ix, tc := range cases { - if !t.Run(fmt.Sprintf("%3d. %s", ix, tc.Description), evendeep.DefaultDeepCopyTestRunner(ix, tc)) { + desc := fmt.Sprintf("%3d. %s", ix, tc.Description) + tr := evendeep.DefaultDeepCopyTestRunner(ix, tc) + if !t.Run(desc, tr) { break } + t.Logf("%5d. passed ----------------", ix) } } diff --git a/cvts.go b/cvts.go index 0f1eb72..6898593 100644 --- a/cvts.go +++ b/cvts.go @@ -2037,12 +2037,35 @@ func (c *fromMapConverter) toStructDirectly(ctx *ValueConverterContext, source, return } +const tryForExportedFieldName = true +const trySmartFieldName = true + func toExportedName(s string) string { if s != "" { a := wordSplitter(s) - for i, word := range a { - a[i] = makeCapitalize1st(word) + + if trySmartFieldName { + // https://go.dev/wiki/CodeReviewComments#initialisms + var smartTrans = map[string]string{ + "tls": "TLS", + "json": "JSON", "toml": "TOML", "yaml": "YAML", "xml": "XML", + "id": "ID", + "url": "URL", "http": "HTTP", "uri": "URI", + "nato": "NATO", + } + for i, word := range a { + if t, ok := smartTrans[string(word)]; ok { + a[i] = []rune(t) + } else { + a[i] = makeCapitalize1st(word) + } + } + } else { + for i, word := range a { + a[i] = makeCapitalize1st(word) + } } + var r []rune for _, word := range a { r = append(r, word...) @@ -2103,6 +2126,34 @@ func (c *fromMapConverter) toStruct(ctx *ValueConverterContext, source reflect.V return } + trySolveTargetName := func(keyStr, targetName string, structType reflect.Type) (tsf reflect.StructField, fieldName string, solved bool) { + // use the key.(string) as the target struct field name + tsf, solved = targetType.FieldByName(targetName) + if !solved { + if tryForExportedFieldName { + if fieldName = toExportedName(keyStr); fieldName != keyStr { + tsf, solved = targetType.FieldByName(fieldName) + } + } + if !solved { + for i := 0; i < structType.NumField(); i++ { + fld := structType.Field(i) + f, r := flags.Parse(fld.Tag, flags.CopyTagName) + if r.Valid() && r.ToName() == keyStr { + tsf, fieldName, solved = fld, fld.Name, true + _ = f + return + } + } + } else { + // ks = fieldName + return + } + } + fieldName = keyStr + return + } + ec := errors.New("map -> struct errors") defer ec.Defer(&err) @@ -2135,22 +2186,25 @@ func (c *fromMapConverter) toStruct(ctx *ValueConverterContext, source reflect.V } } - const tryForExportedFieldName = true - - // use the key.(string) as the target struct field name - tsf, ok := targetType.FieldByName(ks) + tsf, kstmp, ok := trySolveTargetName(ks, ks, targetType) if !ok { - var kS string - if tryForExportedFieldName { - if kS = toExportedName(ks); kS != ks { - tsf, ok = targetType.FieldByName(kS) - } - } - if !ok { - continue - } - ks = kS + continue } + ks = kstmp + // // use the key.(string) as the target struct field name + // tsf, ok := targetType.FieldByName(ks) + // if !ok { + // var kS string + // if tryForExportedFieldName { + // if kS = toExportedName(ks); kS != ks { + // tsf, ok = targetType.FieldByName(kS) + // } + // } + // if !ok { + // continue + // } + // ks = kS + // } fld := target.FieldByName(ks) // dbglog.Log(" fld %q: ", ks) @@ -2161,7 +2215,7 @@ func (c *fromMapConverter) toStruct(ctx *ValueConverterContext, source reflect.V fld = fld.Elem() } else if tsfk == reflect.Ptr { dbglog.Log(" fld.%q: %v (%v)", ks, ref.Valfmt(&fld), ref.Typfmtv(&fld)) - if fld.IsNil() { + if ref.IsNil(fld) { n := reflect.New(fld.Type().Elem()) target.FieldByName(ks).Set(n) fld = target.FieldByName(ks)