created flatnested tag to convert the anonymous field in a flat map (#46)
* flat fields with the new flatten tag * added flatten tag documentation
This commit is contained in:
parent
e5ca5fe90d
commit
24f3e1df2f
17
structs.go
17
structs.go
@ -52,6 +52,12 @@ func New(s interface{}) *Struct {
|
|||||||
// // Map will panic if Animal does not implement String().
|
// // Map will panic if Animal does not implement String().
|
||||||
// Field *Animal `structs:"field,string"`
|
// Field *Animal `structs:"field,string"`
|
||||||
//
|
//
|
||||||
|
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
||||||
|
// in the output map. Example:
|
||||||
|
//
|
||||||
|
// // The FieldStruct's fields will be flattened into the output map.
|
||||||
|
// FieldStruct time.Time `structs:"flatten"`
|
||||||
|
//
|
||||||
// A tag value with the option of "omitnested" stops iterating further if the type
|
// A tag value with the option of "omitnested" stops iterating further if the type
|
||||||
// is a struct. Example:
|
// is a struct. Example:
|
||||||
//
|
//
|
||||||
@ -90,7 +96,7 @@ func (s *Struct) FillMap(out map[string]interface{}) {
|
|||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
name := field.Name
|
name := field.Name
|
||||||
val := s.value.FieldByName(name)
|
val := s.value.FieldByName(name)
|
||||||
|
isSubStruct := false
|
||||||
var finalVal interface{}
|
var finalVal interface{}
|
||||||
|
|
||||||
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
||||||
@ -115,6 +121,7 @@ func (s *Struct) FillMap(out map[string]interface{}) {
|
|||||||
n := New(val.Interface())
|
n := New(val.Interface())
|
||||||
n.TagName = s.TagName
|
n.TagName = s.TagName
|
||||||
m := n.Map()
|
m := n.Map()
|
||||||
|
isSubStruct = true
|
||||||
if len(m) == 0 {
|
if len(m) == 0 {
|
||||||
finalVal = val.Interface()
|
finalVal = val.Interface()
|
||||||
} else {
|
} else {
|
||||||
@ -132,7 +139,13 @@ func (s *Struct) FillMap(out map[string]interface{}) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
out[name] = finalVal
|
if isSubStruct && (tagOpts.Has("flatten")) {
|
||||||
|
for k := range finalVal.(map[string]interface{}) {
|
||||||
|
out[k] = finalVal.(map[string]interface{})[k]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out[name] = finalVal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -296,6 +296,60 @@ func TestMap_Anonymous(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMap_Flatnested(t *testing.T) {
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
a := A{Name: "example"}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A `structs:",flatten"`
|
||||||
|
C int
|
||||||
|
}
|
||||||
|
b := &B{C: 123}
|
||||||
|
b.A = a
|
||||||
|
|
||||||
|
m := Map(b)
|
||||||
|
|
||||||
|
_, ok := m["A"].(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
t.Error("Embedded A struct with tag flatten has to be flat in the map")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedMap := map[string]interface{}{"Name": "example", "C": 123}
|
||||||
|
if !reflect.DeepEqual(m, expectedMap) {
|
||||||
|
t.Errorf("The exprected map %+v does't correspond to %+v", expectedMap, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_FlatnestedOverwrite(t *testing.T) {
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
a := A{Name: "example"}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A `structs:",flatten"`
|
||||||
|
Name string
|
||||||
|
C int
|
||||||
|
}
|
||||||
|
b := &B{C: 123, Name: "bName"}
|
||||||
|
b.A = a
|
||||||
|
|
||||||
|
m := Map(b)
|
||||||
|
|
||||||
|
_, ok := m["A"].(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
t.Error("Embedded A struct with tag flatten has to be flat in the map")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedMap := map[string]interface{}{"Name": "bName", "C": 123}
|
||||||
|
if !reflect.DeepEqual(m, expectedMap) {
|
||||||
|
t.Errorf("The exprected map %+v does't correspond to %+v", expectedMap, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMap_TimeField(t *testing.T) {
|
func TestMap_TimeField(t *testing.T) {
|
||||||
type A struct {
|
type A struct {
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user