dsf11t5u1651 2016-12-09 12:57
浏览 61
已采纳

REST API的策略

In my database, each row corresponds to a struct

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

On the webpage, the user can query on several fields of datum (say account_id and status). The server will return all data that satisfy the query with a projection of the fields (say Id, account_id and status).

Right now, I wrote a HTTP handler to

Extract the query as a datum object from the request:

body, err := ioutil.ReadAll(r.Body)
condition := datum{} 
err = json.Unmarshal(body, &condition)

Use the partially filled datum object to query the database, only the non-nil fields translate to SELECT ... WHERE ..=... The query result is saved in query_result []datum

Write the query_result into json object for reply:

reply := map[string]interface{}{
            "reply": query_result,
        }
data, err := json.Marshal(reply)

The problem is that in the reply many of the fields are nil, but I still send them, which is wasteful. On the other hand, I don't want to change the datum struct to include omitempty tag because in the database a value entry has all fields non-nil.

  • In this case, shall I define a new struct just for the reply? Is there a way to define this new struct using datum struct, instead of hard code one?
  • Is there a better design for this query feature?
  • 写回答

2条回答 默认 最新

  • doufang3001 2016-12-09 18:03
    关注

    You have several options, with choice depending what is more wasteful/expensive in your particular case:

    1. Just use pointers+omitempty in the original struct.
    2. Prepare a custom response object. But you'll need to copy/convert the values from the original struct into its export version.
    3. Write a custom marshaller, that will be exploring your struct and creating an export-ready variant, this way being more dynamic/automatic that #1.

    While #1 needs no comments, and #2 to some extend covered by Gepser above, here's how you can address this with a custom marshaller (the idea is to re-assemble your output skipping nil fields):

    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
    ", 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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分