dongqiao8421 2019-03-25 04:45
浏览 76

如何将结构转换为不带REST API密钥的json

Golang Design Response Struct For API

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))
}

The code above will print

{
  "name": "StackOverflow",
  "description": "Asking Question",
  "Optional": {
    "url": "https://stackoverflow.com"
  }
}

But what i want is this

{
  "name": "StackOverflow",
  "description": "Asking Question",
  "url": "https://stackoverflow.com"
}

I want in the main function i can append some information to json response. Any solution for this design, it prefer that we don't change Response Function. Thank in advance !!

  • 写回答

1条回答 默认 最新

  • duanqiu3800 2019-03-25 04:57
    关注

    You could implement json.Marshaler interface on your ProblemResponse struct, converting everything to a flat map and encoding to JSON. If a type implements json.Marshaler interface, the json encoder will run the MarshalJSON method instead. Here is the documentation: https://golang.org/pkg/encoding/json/#Marshaler

    Example:

    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)
    }
    

    If you don't care about modifying Optional, you could optimize by doing it in place:

    func (p *ProblemResponse) MarshalJSON() ([]byte, error) {
        p.Optional["name"] = p.Name
        p.Optional["description"] = p.Description 
    
        return json.Marshal(p.Optional)
    }
    

    You could write a code generator if you have multiple structure that would need to implement that kind of flattening behaviour on MarshalJSON.

    Alternatively you could use reflection and do something like that (you should complete this method by doing more checks and use json tag), I don't recommend that solution as you loose type safety:

    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))
    

    Or maybe just use a map ?

    评论

报告相同问题?

悬赏问题

  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 正弦信号发生器串并联电路电阻无法保持同步怎么办
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 个人网站被恶意大量访问,怎么办
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM
  • ¥15 划分vlan后不通了
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)