Вы можете реализовать интерфейс json.Marshaler
в своей структуре ProblemResponse
, конвертируя все в плоскую карту и кодируя в JSON.Если тип реализует интерфейс json.Marshaler
, кодировщик json вместо этого будет запускать метод MarshalJSON.Вот документация: https://golang.org/pkg/encoding/json/#Marshaler
Пример:
type Optional map[string]interface{}
type Problem struct {
Name string
Description string
}
type ProblemResponse struct {
Name string `json:"name"`
Description string `json:"description"`
Optional
}
func (p *ProblemResponse) MarshalJSON() ([]byte, error) {
// we create a flat map including problem's field and optional fields
// we copy optional first to make sure name and description are not being overwritten from the optional map
var m = make(map[string]interface{}, 2 + len(p.Optional))
for k, v := range p.Optional {
m[k] = v
}
m["name"] = p.Name
m["description"] = p.Description
return json.Marshal(m)
}
Если вас не волнует изменение Optional
, вы можете оптимизировать, выполнив это in place
:
func (p *ProblemResponse) MarshalJSON() ([]byte, error) {
p.Optional["name"] = p.Name
p.Optional["description"] = p.Description
return json.Marshal(p.Optional)
}
Вы можете написать генератор кода, если у вас есть несколько структур, которые должны были бы реализовать такое поведение flattening
в MarshalJSON.
В качестве альтернативы вы можете использовать отражение и сделать что-то подобное (выследует завершить этот метод, выполнив больше проверок и используя json tag), я не рекомендую это решение, поскольку вы теряете безопасность типов:
func Flatten(s interface{}) map[string]interface{} {
v := reflect.ValueOf(s)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
panic(fmt.Errorf("expect struct %T given", s))
}
t := v.Type()
nField := t.NumField()
r := v.FieldByName("Optional").Interface().(Optional)
for i := 0; i < nField; i++ {
f := t.Field(i)
if f.Name != "Optional" {
fv := v.Field(i)
// here you could read json tags
// to get the value in the json tag instead of ToLower
r[strings.ToLower(f.Name)] = fv.Interface()
}
}
return r
}
// usage
b, err i:= json.Marshal(Flatten(problemRes))
Или, может быть, просто используйте карту?