Thanks, that got me in the right direction. As a first approximation I use something like this:
func mapOrSliceOf(value interface{}) interface{} {
v := reflect.Indirect(reflect.ValueOf(value))
switch v.Kind() {
case reflect.Slice, reflect.Array:
switch v.Type().Elem().Kind() {
case reflect.Uint8:
return v.Interface()
default:
var a []interface{}
for i := 0; i < v.Len(); i++ {
a = append(a, mapOrSliceOf(v.Index(i).Interface()))
}
return a
}
case reflect.Struct:
switch v.Type().PkgPath() + "." + v.Type().Name() {
case "time.Time":
return v.Interface()
default:
a := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
a[v.Type().Field(i).Name] = mapOrSliceOf(v.Field(i).Interface())
}
return a
}
case reflect.Bool, reflect.Int, reflect.Uint, reflect.Float32, reflect.Float64, reflect.String:
return v.Interface()
default:
panic("mapOrSliceOf: cannot handle type " + v.Kind().String())
}
return nil
}