json - 如何在没有 rest API key 的情况下将结构转换为 json

标签 json go struct

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/

相关文章:

c - Malloc 结构内的数组

ruby-on-rails - 尝试发送电子邮件时出现 sidekiq_mailer 错误

.net - 枚举序列化 Json 与 XML

go - 一次检查golang中的所有数据键

go - Beego POST方法始终寻找模板文件

c++ - 在类内部声明结构

jQuery Ajax 访问 json

java - 如何在 Jackson 自定义 JSON 反序列化器中使用常规 JSON 解释

go - posFlag 需要什么样的编码?

hadoop - 如何将数据插入 Hive 中的复杂数据类型 "Struct"