From 23c2f126724918d43cf557e6cbbad050c3ea2d30 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 9 Apr 2020 13:37:27 +0800 Subject: [PATCH] improve MapDeep function for package gconv; improve gjson.New function for loading struct parameter --- .example/other/test.go | 38 ++++++----- encoding/gjson/gjson_api_new_load.go | 2 +- internal/structs/structs_map.go | 2 + internal/structs/structs_tag.go | 7 +++ util/gconv/gconv_map.go | 28 ++++++--- util/gconv/gconv_z_unit_map_test.go | 94 +++++++++++++++++++++++++++- 6 files changed, 144 insertions(+), 27 deletions(-) diff --git a/.example/other/test.go b/.example/other/test.go index 1ffd72098..4db2d3e70 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -3,21 +3,29 @@ package main import ( "encoding/json" "fmt" - "gopkg.in/yaml.v3" + "github.com/gogf/gf/encoding/gjson" ) -func main() { - data := []byte(` -m: - k: v - `) - var result map[string]interface{} - if err := yaml.Unmarshal(data, &result); err != nil { - panic(err) - } - b, err := json.Marshal(result) - if err != nil { - panic(err) - } - fmt.Println(string(b)) +type A struct { + D string + E string +} +type B struct { + A `json:"a"` + F string +} + +func SystemJsonEncode(a interface{}) string { + js, err := json.Marshal(a) + if err != nil { + return "{}" + } else { + return fmt.Sprintf("%s", js) + } +} + +func main() { + var b B + fmt.Println(SystemJsonEncode(b)) + fmt.Println(gjson.New(b).MustToJsonString()) } diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index 44bc82fe3..680792235 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -74,7 +74,7 @@ func NewWithTag(data interface{}, tags string, safe ...bool) *Json { i := interface{}(nil) // Note that it uses Map function implementing the converting. // Note that it here should not use MapDeep function if you really know what it means. - i = gconv.Map(data, tags) + i = gconv.MapDeep(data, tags) j = &Json{ p: &i, c: byte(gDEFAULT_SPLIT_CHAR), diff --git a/internal/structs/structs_map.go b/internal/structs/structs_map.go index 766625166..012855c6e 100644 --- a/internal/structs/structs_map.go +++ b/internal/structs/structs_map.go @@ -14,6 +14,8 @@ import ( // MapField retrieves struct field as map[name/tag]*Field from , and returns the map. // +// The parameter should be type of struct/*struct. +// // The parameter specifies the priority tag array for retrieving from high to low. // // The parameter specifies whether retrieving the struct field recursively. diff --git a/internal/structs/structs_tag.go b/internal/structs/structs_tag.go index 1fbfc2a54..fdce13fde 100644 --- a/internal/structs/structs_tag.go +++ b/internal/structs/structs_tag.go @@ -14,6 +14,8 @@ import ( // TagFields retrieves struct tags as []*Field from , and returns it. // +// The parameter should be type of struct/*struct. +// // The parameter specifies whether retrieving the struct field recursively. // // Note that it only retrieves the exported attributes with first letter up-case from struct. @@ -23,6 +25,7 @@ func TagFields(pointer interface{}, priority []string, recursive bool) []*Field // doTagFields retrieves the tag and corresponding attribute name from . It also filters repeated // tag internally. +// The parameter should be type of struct/*struct. func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap map[string]struct{}) []*Field { var fields []*structs.Field if v, ok := pointer.(reflect.Value); ok { @@ -85,6 +88,8 @@ func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap // TagMapName retrieves struct tags as map[tag]attribute from , and returns it. // +// The parameter should be type of struct/*struct. +// // The parameter specifies whether retrieving the struct field recursively. // // Note that it only retrieves the exported attributes with first letter up-case from struct. @@ -99,6 +104,8 @@ func TagMapName(pointer interface{}, priority []string, recursive bool) map[stri // TagMapField retrieves struct tags as map[tag]*Field from , and returns it. // +// The parameter should be type of struct/*struct. +// // The parameter specifies whether retrieving the struct field recursively. // // Note that it only retrieves the exported attributes with first letter up-case from struct. diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index 695f9ac54..e50ca500b 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -158,9 +158,14 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] return v.MapStrAny() } // Using reflect for converting. - rt := rv.Type() - name := "" - tagArray := structTagPriority + var ( + rtField reflect.StructField + rvField reflect.Value + rvKind reflect.Kind + rt = rv.Type() + name = "" + tagArray = structTagPriority + ) switch len(tags) { case 0: // No need handle. @@ -169,9 +174,6 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] default: tagArray = append(tags, structTagPriority...) } - var rtField reflect.StructField - var rvField reflect.Value - var rvKind reflect.Kind for i := 0; i < rv.NumField(); i++ { rtField = rt.Field(i) rvField = rv.Field(i) @@ -188,7 +190,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] } } if name == "" { - name = strings.TrimSpace(fieldName) + name = fieldName } else { // Support json tag feature: -, omitempty name = strings.TrimSpace(name) @@ -216,9 +218,17 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] rvKind = rvField.Kind() } if rvKind == reflect.Struct { - for k, v := range doMapConvert(rvField.Interface(), recursive, tags...) { - m[k] = v + if name == fieldName { + // It means this attribute field has no tag. + // Overwrite the attribute with sub-struct attribute fields. + for k, v := range doMapConvert(rvField.Interface(), recursive, tags...) { + m[k] = v + } + } else { + // It means this attribute field has desired tag. + m[name] = doMapConvert(rvField.Interface(), recursive, tags...) } + } else { m[name] = rvField.Interface() } diff --git a/util/gconv/gconv_z_unit_map_test.go b/util/gconv/gconv_z_unit_map_test.go index 5f4176c0e..0a3bfc24d 100644 --- a/util/gconv/gconv_z_unit_map_test.go +++ b/util/gconv/gconv_z_unit_map_test.go @@ -54,7 +54,7 @@ func Test_Map_Slice(t *testing.T) { }) } -func Test_Map_StructWithGconvTag(t *testing.T) { +func Test_Map_StructWithGConvTag(t *testing.T) { gtest.C(t, func(t *gtest.T) { type User struct { Uid int @@ -179,7 +179,7 @@ func Test_Map_PrivateAttribute(t *testing.T) { }) } -func Test_Map_StructInherit(t *testing.T) { +func Test_MapDeep(t *testing.T) { type Ids struct { Id int `c:"id"` Uid int `c:"uid"` @@ -216,6 +216,43 @@ func Test_Map_StructInherit(t *testing.T) { }) } +func Test_MapDeepWithAttributeTag(t *testing.T) { + type Ids struct { + Id int `c:"id"` + Uid int `c:"uid"` + } + type Base struct { + Ids `json:"ids"` + CreateTime string `c:"create_time"` + } + type User struct { + Base `json:"base"` + Passport string `c:"passport"` + Password string `c:"password"` + Nickname string `c:"nickname"` + } + gtest.C(t, func(t *gtest.T) { + user := new(User) + user.Id = 100 + user.Nickname = "john" + user.CreateTime = "2019" + m := gconv.Map(user) + t.Assert(m["id"], "") + t.Assert(m["nickname"], user.Nickname) + t.Assert(m["create_time"], "") + }) + gtest.C(t, func(t *gtest.T) { + user := new(User) + user.Id = 100 + user.Nickname = "john" + user.CreateTime = "2019" + m := gconv.MapDeep(user) + t.Assert(m["base"].(map[string]interface{})["ids"].(map[string]interface{})["id"], user.Id) + t.Assert(m["nickname"], user.Nickname) + t.Assert(m["base"].(map[string]interface{})["create_time"], user.CreateTime) + }) +} + func Test_MapToMap(t *testing.T) { type User struct { Id int @@ -454,3 +491,56 @@ func Test_MapToMapsDeep(t *testing.T) { t.Assert(m["200"][1].Name, "jim") }) } + +func Test_MapToMapsDeepWithTag(t *testing.T) { + type Ids struct { + Id int + Uid int + } + type Base struct { + Ids `json:"ids"` + Time string + } + type User struct { + Base `json:"base"` + Name string + } + params := g.MapIntAny{ + 100: g.Slice{ + g.Map{"id": 1, "name": "john"}, + g.Map{"id": 2, "name": "smith"}, + }, + 200: g.Slice{ + g.Map{"id": 3, "name": "green"}, + g.Map{"id": 4, "name": "jim"}, + }, + } + gtest.C(t, func(t *gtest.T) { + m := make(map[string][]*User) + err := gconv.MapToMaps(params, &m) + t.Assert(err, nil) + t.Assert(len(m), 2) + t.Assert(m["100"][0].Id, 0) + t.Assert(m["100"][1].Id, 0) + t.Assert(m["100"][0].Name, "john") + t.Assert(m["100"][1].Name, "smith") + t.Assert(m["200"][0].Id, 0) + t.Assert(m["200"][1].Id, 0) + t.Assert(m["200"][0].Name, "green") + t.Assert(m["200"][1].Name, "jim") + }) + gtest.C(t, func(t *gtest.T) { + m := make(map[string][]*User) + err := gconv.MapToMapsDeep(params, &m) + t.Assert(err, nil) + t.Assert(len(m), 2) + t.Assert(m["100"][0].Id, 1) + t.Assert(m["100"][1].Id, 2) + t.Assert(m["100"][0].Name, "john") + t.Assert(m["100"][1].Name, "smith") + t.Assert(m["200"][0].Id, 3) + t.Assert(m["200"][1].Id, 4) + t.Assert(m["200"][0].Name, "green") + t.Assert(m["200"][1].Name, "jim") + }) +}