如何将结构转换为不带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 !!

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 ?