Reputation: 219
I am having trouble iterating over slices of interfaces that contain slices of interfaces.
This problem has arisen through attempting to work with an API call which returns JSON data. There is quite a lot of data returned and the structure differs quite dramatically depending on the request. There is also no structure for the JSON responses in the API documentation so I am trying to implement some methods for working with arbitrary JSON responses.
Presently when the initial call is made it is dropped into a map[string]interface{} and then a switch statement is run to determine the type of each element, the problem occurs when a slice of interfaces is encountered. I can't seem to do anything with them.
I have tried using the sort package a few times (specifically the sort and slicestable functions) to no avail.
The error I am receiving is:
interface conversion: interface {} is []interface {}, not map[string]interface {}
Which occurs when I try and map the slice of interfaces so I can iterate over them with the switch statement again.
output := make(map[string]interface{})
err = json.Unmarshal(body, &output)
fmt.Println(err)
identify(output)
return err
}
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 []interface{}:
fmt.Println(" is []interface ", bb)
test := b.(map[string]interface{}) // falis here
fmt.Println("Success!", test)
default:
return
}
}
}
So the basic question is how do I iterate over nested slices of interfaces without knowing the structure beforehand?
Upvotes: 1
Views: 2810
Reputation: 12675
You can add a switch case which is checking the type of interface for slice of interfaces and then run the same function as recursive until whole json is parsed.
output := make(map[string]interface{})
err = json.Unmarshal(body, &output)
fmt.Println(err)
identify(output)
return err
}
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 []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
}
}
}
There can be deep nested json. We just needs to create options for each case until json is completely parsed.
Upvotes: 1
Reputation: 9541
Well, you can't cast []interface{}
to map[string]interface{}
. Since it's a slice, you can iterate over its elements (note that bb
is b
already cast to the appropriate type and you don't need to cast b
again):
case []interface{}:
fmt.Println(" is []interface ", bb)
for _, val := range bb {
// do something with val
}
default:
...
Why do you have to deal with something that you don't know? Is it possible that you reconsider your architecture and work with known type(s)?
have you seen the example of delaying unmarshaling using json.RawMessage
? Or maybe try implementing the Unmarshaler interface?
Upvotes: 1