improve MapDeep function for package gconv; improve gjson.New function for loading struct parameter

This commit is contained in:
John
2020-04-09 13:37:27 +08:00
parent 7fd53673ce
commit 23c2f12672
6 changed files with 144 additions and 27 deletions

View File

@ -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())
}

View File

@ -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),

View File

@ -14,6 +14,8 @@ import (
// MapField retrieves struct field as map[name/tag]*Field from <pointer>, and returns the map.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <priority> specifies the priority tag array for retrieving from high to low.
//
// The parameter <recursive> specifies whether retrieving the struct field recursively.

View File

@ -14,6 +14,8 @@ import (
// TagFields retrieves struct tags as []*Field from <pointer>, and returns it.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <recursive> 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 <pointer>. It also filters repeated
// tag internally.
// The parameter <pointer> 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 <pointer>, and returns it.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <recursive> 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 <pointer>, and returns it.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <recursive> specifies whether retrieving the struct field recursively.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.

View File

@ -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()
}

View File

@ -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")
})
}