Reputation: 853
I have a nested three layer struct. I would like to use reflect in Go to parse it (use recursive function). The reasons to use reflect and the recursive function are
Here are some codes.
type Edge struct{
Uid string `json:"uid,omitempty"`
Name string `json:"name,omitempty"`
Read Article `json:"visited,omitempty"`
}
type Article struct {
Uid string`json:"uid,omitempty"`
Namestring`json:"name,omitempty"`
From Site `json:"from,omitempty"`
}
type Site struct{
Uid string `json:"uid,omitempty"`
Name string `json:"name,omitempty"`
}
func CheckNestedStruct(edges interface{}){
rv := reflect.ValueOf(edges).Elem()
uidField := rv.FieldByName("Uid")
uid := getStructField(edges, "Name") // get value of Name from database
if (uid != ""){
uidField.SetString(uid)
}
for i := 0 ; i < rv.NumField() ; i++ {
field := rv.Field(i)
fieldType := field.Kind()
if (fieldType == reflect.Struct){
CheckNestedStruct(field)
}
}
}
func main(){
....
var edges Edges{
...
...
}
CheckNestedStruct(&edges)
}
When I ran this, in the first layer I got "type: *entity.SacWebIS". However, in the second iteration/recursion, I got "type: *reflect.rtype" .
I also tried field.Interface()
.
How to modify this code?
Thanks.
UPDATE
The solution is
CheckNestedStruct(dg, field.Addr().Interface())
Upvotes: 7
Views: 10605
Reputation: 75
A simpler version of the above code (iterate function):
func iterate(data interface{}) interface{} {
d := reflect.ValueOf(data)
if reflect.ValueOf(data).Kind() == reflect.Slice {
returnSlice := make([]interface{}, d.Len())
for i := 0; i < d.Len(); i++ {
returnSlice[i] = iterate(d.Index(i).Interface())
}
return returnSlice
} else if reflect.ValueOf(data).Kind() == reflect.Map {
tmpData := make(map[string]interface{})
for _, k := range d.MapKeys() {
tmpData[k.String()] = iterate(d.MapIndex(k).Interface())
}
return tmpData
} else {
return data
}
}
Upvotes: 0
Reputation: 12675
For parsing unknown json without knowing the values and types of the field you needs to create a recursive function which will parse through deeply nested underlying value. You can get final value using type assertion .
func main() {
m, ok := myJson.(map[string]interface{})
newM := iterate(m)
jsonBytes, err := json.Marshal(newM)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(jsonBytes))
}
For the record on unmarshaling a json to an interface It converts mainly in two types for nested structure either slice of interface []interface{}
or map of interface map[string]interface{}
until we get final value of nested structure which we can get using Interface()
.Hence we can create a recursive for deep nested structures.
func iterate(data interface{}) interface{} {
if reflect.ValueOf(data).Kind() == reflect.Slice {
d := reflect.ValueOf(data)
tmpData := make([]interface{}, d.Len())
returnSlice := make([]interface{}, d.Len())
for i := 0; i < d.Len(); i++ {
tmpData[i] = d.Index(i).Interface()
}
for i, v := range tmpData {
returnSlice[i] = iterate(v)
}
return returnSlice
} else if reflect.ValueOf(data).Kind() == reflect.Map {
d := reflect.ValueOf(data)
tmpData := make(map[string]interface{})
for _, k := range d.MapKeys() {
typeOfValue := reflect.TypeOf(d.MapIndex(k).Interface()).Kind()
if typeOfValue == reflect.Map || typeOfValue == reflect.Slice {
tmpData[k.String()] = iterate(d.MapIndex(k).Interface())
} else {
tmpData[k.String()] = d.MapIndex(k).Interface()
}
}
return tmpData
}
return data
}
At last for underlying value of an interface{} which will be of primitive type string
, float64
, bool
.
func identify(output map[string]interface{}) {
fmt.Printf("%T", output)
for a, b := range output {
switch bb := b.(type) {
case string:
fmt.Println("This is a string")
case float64:
fmt.Println("this is a float")
case bool:
fmt.Println("this is a boolean")
case []interface{}:
// Access the values in the JSON object and place them in an Item
for _, itemValue := range jsonObj {
fmt.Printf("%v is an interface\n", itemValue)
identify(itemValue.(map[string]interface{}))
}
default:
return
}
}
}
Check it on Go Playground
Upvotes: 2
Reputation: 109357
You are calling reflect.ValueOf
on a reflect.Value
, which is what gives you the type *reflect.rtype
. If you want to pass the reflect.Value
back to the same function, you need to first call Interface()
.
CheckNestedStruct(field.Interface())
You then are calling Elem
regardless of whether you're operating on a pointer or a value. If you want to conditionally indirect a value, use reflect.Indirect
rv := reflect.Indirect(reflect.ValueOf(edges))
Upvotes: 8