在我的数据库中,每一行对应一个结构

type datum struct{
    Id *string `json:"task_id"`
    Status *string `json:"status"`
    AccountId *string `json:"account_id"`
    .... // many more fields, all of pointer types
}

用户可以在网页上查询基准的几个字段(例如account_idstatus)。服务器将返回所有满足查询条件的数据,并带有字段的投影(例如Idaccount_idstatus)。

现在,我写了一个HTTP处理程序

从请求中将查询提取为基准对象:
body, err := ioutil.ReadAll(r.Body)
condition := datum{}
err = json.Unmarshal(body, &condition)

使用部分填充的基准对象来查询数据库,只有非nil字段才转换为SELECT ... WHERE ..=..。查询结果保存在query_result []datum

query_result写入json对象以进行回复:
reply := map[string]interface{}{
            "reply": query_result,
        }
data, err := json.Marshal(reply)

问题在于,在答复中,许多字段为零,但是我仍然发送它们,这很浪费。另一方面,我不想更改基准结构以包括omitempty标记,因为在数据库中,值条目的所有字段均为非零。
  • 在这种情况下,我是否应该仅为回复定义新的结构?有没有一种方法可以使用datum结构而不是硬代码来定义此新结构?
  • 此查询功能是否有更好的设计?
  • 最佳答案

    您有几种选择,根据您的特定情况选择哪种更为浪费/昂贵:

  • 只需在原始结构中使用pointers + omitempty。
  • 准备一个自定义响应对象。但是,您需要将值从原始结构复制/转换为其导出版本。
  • 编写一个自定义编码器,它将探索您的结构并创建一个可导出的变体,这种方式比#1更具动态/自动性。

  • 尽管#1不需要注释,而#2则在上面的Gepser中有一定程度的扩展,但是您可以通过以下方法使用自定义编码器解决此问题(想法是重新组装输出,跳过nil个字段):
    package main
    
    import (
        "fmt"
    
        "encoding/json"
        "reflect"
    )
    
    type datum struct {
        Id        *string `json:"task_id"`
        Status    *string `json:"status"`
        AccountId *string `json:"account_id"`
    }
    
    type Response struct {
        Reply []datum `json:"reply"`
    }
    
    func main() {
    
        var query_result []datum
    
        // mocking a query result with records with nil fields
        val_id_a := "id-a"
        val_status := "status-b"
        d1 := datum{
            Id:     &val_id_a,
            Status: &val_status,
        }
    
        query_result = append(query_result, d1)
    
        val_id_b := "id-b"
        val_account_id := "account-id-b"
        d2 := datum{
            Id:        &val_id_b,
            AccountId: &val_account_id,
        }
    
        query_result = append(query_result, d2)
    
        reply := &Response{
            Reply: query_result,
        }
    
        data, err := json.Marshal(reply)
        if err != nil {
            panic(err)
        }
    
        fmt.Printf("%+v\n", string(data))
    }
    
    // MarshalJSON is a custom JSON marshaller implementation for Response object.
    func (r *Response) MarshalJSON() ([]byte, error) {
        a := struct {
            Reply []map[string]interface{} `json:"reply"`
        }{}
    
        for _, v := range r.Reply {
            a.Reply = append(a.Reply, converter(v))
        }
    
        return json.Marshal(a)
    }
    
    // converter converts a struct into a map, skipping fields with nil values.
    func converter(in interface{}) map[string]interface{} {
        out := make(map[string]interface{})
        v := reflect.ValueOf(in)
    
        for i := 0; i < v.NumField(); i++ {
            f := v.Type().Field(i)
            tag := f.Tag.Get("json")
            if tag != "" && !v.Field(i).IsNil() {
                out[tag] = v.Field(i).Interface()
            }
        }
        return out
    }
    

    关于database - REST API的策略,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41061042/

    10-12 05:03