parent
5ada2f449b
commit
f7e369b900
84
structs.go
84
structs.go
@ -115,17 +115,17 @@ func (s *Struct) FillMap(out map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
if !tagOpts.Has("omitnested") {
|
||||||
// look out for embedded structs, and convert them to a
|
finalVal = s.nested(val)
|
||||||
// map[string]interface{} too
|
|
||||||
n := New(val.Interface())
|
v := reflect.ValueOf(val.Interface())
|
||||||
n.TagName = s.TagName
|
if v.Kind() == reflect.Ptr {
|
||||||
m := n.Map()
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Map, reflect.Struct:
|
||||||
isSubStruct = true
|
isSubStruct = true
|
||||||
if len(m) == 0 {
|
|
||||||
finalVal = val.Interface()
|
|
||||||
} else {
|
|
||||||
finalVal = m
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
finalVal = val.Interface()
|
finalVal = val.Interface()
|
||||||
@ -505,3 +505,67 @@ func IsStruct(s interface{}) bool {
|
|||||||
func Name(s interface{}) string {
|
func Name(s interface{}) string {
|
||||||
return New(s).Name()
|
return New(s).Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nested retrieves recursively all types for the given value and returns the
|
||||||
|
// nested value.
|
||||||
|
func (s *Struct) nested(val reflect.Value) interface{} {
|
||||||
|
var finalVal interface{}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(val.Interface())
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
n := New(val.Interface())
|
||||||
|
n.TagName = s.TagName
|
||||||
|
m := n.Map()
|
||||||
|
|
||||||
|
// do not add the converted value if there are no exported fields, ie:
|
||||||
|
// time.Time
|
||||||
|
if len(m) == 0 {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
} else {
|
||||||
|
finalVal = m
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
v := val.Type().Elem()
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// only iterate over struct types, ie: map[string]StructType,
|
||||||
|
// map[string][]StructType,
|
||||||
|
if v.Kind() == reflect.Struct ||
|
||||||
|
(v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Struct) {
|
||||||
|
m := make(map[string]interface{}, val.Len())
|
||||||
|
for _, k := range val.MapKeys() {
|
||||||
|
m[k.String()] = s.nested(val.MapIndex(k))
|
||||||
|
}
|
||||||
|
finalVal = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(arslan): should this be optional?
|
||||||
|
finalVal = val.Interface()
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
// TODO(arslan): should this be optional?
|
||||||
|
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||||
|
// []string, co... We only iterate further if it's a struct.
|
||||||
|
if val.Type().Elem().Kind() != reflect.Struct {
|
||||||
|
finalVal = val.Interface()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
slices := make([]interface{}, val.Len(), val.Len())
|
||||||
|
for x := 0; x < val.Len(); x++ {
|
||||||
|
slices[x] = s.nested(val.Index(x))
|
||||||
|
}
|
||||||
|
finalVal = slices
|
||||||
|
default:
|
||||||
|
finalVal = val.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalVal
|
||||||
|
}
|
||||||
|
|||||||
233
structs_test.go
233
structs_test.go
@ -268,6 +268,239 @@ func TestMap_Nested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMap_NestedMapWithStructValues(t *testing.T) {
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
A map[string]*A
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &A{Name: "example"}
|
||||||
|
|
||||||
|
b := &B{
|
||||||
|
A: map[string]*A{
|
||||||
|
"example_key": a,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
m := Map(b)
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
|
||||||
|
t.Errorf("Map should return a map type, got: %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
in, ok := m["A"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["A"])
|
||||||
|
}
|
||||||
|
|
||||||
|
example := in["example_key"].(map[string]interface{})
|
||||||
|
if name := example["Name"].(string); name != "example" {
|
||||||
|
t.Errorf("Map nested struct's name field should give example, got: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_NestedMapWithStringValues(t *testing.T) {
|
||||||
|
type B struct {
|
||||||
|
Foo map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
B *B
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &B{
|
||||||
|
Foo: map[string]string{
|
||||||
|
"example_key": "example",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &A{B: b}
|
||||||
|
|
||||||
|
m := Map(a)
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
|
||||||
|
t.Errorf("Map should return a map type, got: %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
in, ok := m["B"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"])
|
||||||
|
}
|
||||||
|
|
||||||
|
foo := in["Foo"].(map[string]string)
|
||||||
|
if name := foo["example_key"]; name != "example" {
|
||||||
|
t.Errorf("Map nested struct's name field should give example, got: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestMap_NestedMapWithInterfaceValues(t *testing.T) {
|
||||||
|
type B struct {
|
||||||
|
Foo map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
B *B
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &B{
|
||||||
|
Foo: map[string]interface{}{
|
||||||
|
"example_key": "example",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &A{B: b}
|
||||||
|
|
||||||
|
m := Map(a)
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
|
||||||
|
t.Errorf("Map should return a map type, got: %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
in, ok := m["B"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"])
|
||||||
|
}
|
||||||
|
|
||||||
|
foo := in["Foo"].(map[string]interface{})
|
||||||
|
if name := foo["example_key"]; name != "example" {
|
||||||
|
t.Errorf("Map nested struct's name field should give example, got: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_NestedMapWithSliceIntValues(t *testing.T) {
|
||||||
|
type B struct {
|
||||||
|
Foo map[string][]int
|
||||||
|
}
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
B *B
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &B{
|
||||||
|
Foo: map[string][]int{
|
||||||
|
"example_key": []int{80},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &A{B: b}
|
||||||
|
|
||||||
|
m := Map(a)
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
|
||||||
|
t.Errorf("Map should return a map type, got: %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
in, ok := m["B"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"])
|
||||||
|
}
|
||||||
|
|
||||||
|
foo := in["Foo"].(map[string][]int)
|
||||||
|
if name := foo["example_key"]; name[0] != 80 {
|
||||||
|
t.Errorf("Map nested struct's name field should give example, got: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_NestedMapWithSliceStructValues(t *testing.T) {
|
||||||
|
type address struct {
|
||||||
|
Country string `structs:"country"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
Foo map[string][]address
|
||||||
|
}
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
B *B
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &B{
|
||||||
|
Foo: map[string][]address{
|
||||||
|
"example_key": []address{
|
||||||
|
{Country: "Turkey"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &A{B: b}
|
||||||
|
m := Map(a)
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
|
||||||
|
t.Errorf("Map should return a map type, got: %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
in, ok := m["B"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"])
|
||||||
|
}
|
||||||
|
|
||||||
|
foo := in["Foo"].(map[string]interface{})
|
||||||
|
|
||||||
|
addresses := foo["example_key"].([]interface{})
|
||||||
|
|
||||||
|
addr, ok := addresses[0].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Nested type of map should be of type map[string]interface{}, have %T", m["B"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := addr["country"]; !exists {
|
||||||
|
t.Errorf("Expecting country, but found Country")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_NestedSliceWithStructValues(t *testing.T) {
|
||||||
|
type address struct {
|
||||||
|
Country string `structs:"customCountryName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type person struct {
|
||||||
|
Name string `structs:"name"`
|
||||||
|
Addresses []address `structs:"addresses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
p := person{
|
||||||
|
Name: "test",
|
||||||
|
Addresses: []address{
|
||||||
|
address{Country: "England"},
|
||||||
|
address{Country: "Italy"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mp := Map(p)
|
||||||
|
|
||||||
|
mpAddresses := mp["addresses"].([]interface{})
|
||||||
|
if _, exists := mpAddresses[0].(map[string]interface{})["Country"]; exists {
|
||||||
|
t.Errorf("Expecting customCountryName, but found Country")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := mpAddresses[0].(map[string]interface{})["customCountryName"]; !exists {
|
||||||
|
t.Errorf("customCountryName key not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_NestedSliceWithIntValues(t *testing.T) {
|
||||||
|
type person struct {
|
||||||
|
Name string `structs:"name"`
|
||||||
|
Ports []int `structs:"ports"`
|
||||||
|
}
|
||||||
|
|
||||||
|
p := person{
|
||||||
|
Name: "test",
|
||||||
|
Ports: []int{80},
|
||||||
|
}
|
||||||
|
m := Map(p)
|
||||||
|
|
||||||
|
ports, ok := m["ports"].([]int)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Nested type of map should be of type []int, have %T", m["ports"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if ports[0] != 80 {
|
||||||
|
t.Errorf("Map nested struct's ports field should give 80, got: %v", ports)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMap_Anonymous(t *testing.T) {
|
func TestMap_Anonymous(t *testing.T) {
|
||||||
type A struct {
|
type A struct {
|
||||||
Name string
|
Name string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user