API 的 Golang 设计响应结构
package main
import (
"encoding/json"
"fmt"
)
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 (problem *Problem) ToRes() *ProblemResponse {
return &ProblemResponse{
Name: problem.Name,
Description: problem.Description,
}
}
func main() {
problem := Problem{"StackOverflow", "Asking Question"}
problemRes := problem.ToRes()
problemRes.Optional = make(map[string]interface{})
problemRes.Optional["url"] = "https://stackoverflow.com"
Response(*problemRes)
}
func Response(obj interface{}) {
data, _ := json.Marshal(obj)
fmt.Println(string(data))
}
上面的代码会打印
{
"name": "StackOverflow",
"description": "Asking Question",
"Optional": {
"url": "https://stackoverflow.com"
}
}
但是我想要的是这个
{
"name": "StackOverflow",
"description": "Asking Question",
"url": "https://stackoverflow.com"
}
我想在主函数中将一些信息附加到 json 响应中。这种设计的任何解决方案,它更喜欢我们不改变响应函数。提前致谢!!
最佳答案
您可以在 ProblemResponse
结构上实现 json.Marshaler
接口(interface),将所有内容转换为平面映射并编码为 JSON。如果一个类型实现了 json.Marshaler
接口(interface),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
,您可以通过就地
进行优化:
func (p *ProblemResponse) MarshalJSON() ([]byte, error) {
p.Optional["name"] = p.Name
p.Optional["description"] = p.Description
return json.Marshal(p.Optional)
}
如果您有多个结构需要在 MarshalJSON 上实现这种扁平化
行为,您可以编写一个代码生成器。
或者你可以使用反射并做类似的事情(你应该通过做更多的检查来完成这个方法并使用 json 标签),我不推荐这种解决方案,因为你失去了类型安全:
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))
或者也许只使用 map ?
关于json - 如何在没有 rest API key 的情况下将结构转换为 json,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55331391/