You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang/Gin/GORM中类似Django序列化器的方案问询

Go中控制序列化/反序列化字段的最优方案

背景

我平时用Python/Django技术栈,习惯了用Serializer灵活控制请求体的字段处理和响应体的返回字段,但转到Golang/Gin/GORM后,没找到类似的工具。同时看到不少人不推荐用DTO(数据传输对象),所以想请教Go里有没有合适的序列化/反序列化库,或者靠谱的实践模式。

业务场景

  • /user/:id/update/ 接口:必须忽略用户传入的password字段;
  • /user 列表接口:响应里绝对不能包含password字段。

已尝试的方案

  1. 用json:"-"标签:但因为有单独更新密码的接口,这个方案直接废掉了;
  2. 写了个泛型函数ToStruct:把指定字段设为零值,配合omitempty标签来隐藏字段,但不确定这是不是最优解。函数代码如下:
func ToStruct[T any](s T, flds []string) T {
    elem := reflect.ValueOf(s).Elem()
    for i:=0; i<elem.NumField(); i++ {
        if slices.Contains(flds, elem.Type().Field(i).Name) {
            f := elem.Field(i)
            if f.Kind() == reflect.Ptr {
                // 把字段设为nil
                f.Set(reflect.Zero(f.Type()))
            } else {
                // 把字段设为对应类型的零值
                f.SetZero()
            }
        }
    }
    return s
}

推荐解决方案

1. 用JSON标签+指针类型做条件序列化

Go的json标签结合指针类型和omitempty,能轻松实现不同场景的字段控制:

  • 定义User结构体时,把Password设为指针类型:
type User struct {
    ID       uint   `json:"id"`
    Username string `json:"username"`
    Password *string `json:"password,omitempty"`
}
  • 处理/user列表接口时,直接把Password设为nil,序列化的时候就会自动忽略这个字段;
  • 处理/user/:id/update/接口时,反序列化后如果Password是nil,就跳过更新密码的逻辑;单独更新密码的接口则正常接收字段值就行。

2. 用第三方序列化库做灵活过滤

有些第三方库支持直接指定序列化/反序列化时要包含或排除的字段,不用改结构体:

  • go-json:序列化时可以直接指定排除的字段:
// 序列化时排除password字段
data, err := json.MarshalWithOptions(user, json.Exclude("Password"))
  • goccy/go-json:不仅支持字段过滤,序列化性能也比标准库好。

3. 合理用DTO(别被网上的说法吓到)

虽然很多人吐槽DTO会重复代码,但在复杂业务场景下,它是最清晰、最不容易出错的方案:

  • 给不同接口单独定义DTO结构体:
// 更新用户的请求DTO(不含password,自然忽略前端传入的值)
type UpdateUserRequest struct {
    ID       uint   `json:"id"`
    Username string `json:"username"`
}

// 用户列表响应DTO(不含password)
type UserListResponse struct {
    ID       uint   `json:"id"`
    Username string `json:"username"`
}

// 单独更新密码的DTO
type UpdatePasswordRequest struct {
    ID       uint   `json:"id"`
    Password string `json:"password"`
}

这种方式代码可读性拉满,后期维护也省心,业务逻辑复杂的时候特别好用。

4. 优化你的泛型函数

如果不想用DTO或第三方库,可以优化你写的泛型函数,比如改成按JSON标签名来排除字段,适配更灵活:

import "strings"

func ExcludeFields[T any](s T, excludeJSONTags []string) T {
    elem := reflect.ValueOf(s).Elem()
    typ := elem.Type()
    for i := 0; i < elem.NumField(); i++ {
        field := typ.Field(i)
        // 解析JSON标签的主名称
        jsonTag := strings.Split(field.Tag.Get("json"), ",")[0]
        if slices.Contains(excludeJSONTags, jsonTag) {
            f := elem.Field(i)
            if f.Kind() == reflect.Ptr {
                f.Set(reflect.Zero(f.Type()))
            } else {
                f.SetZero()
            }
        }
    }
    return s
}

调用的时候直接传JSON标签名就行:

// 排除password字段
user = ExcludeFields(user, []string{"password"})

内容的提问来源于stack exchange,提问作者fady malak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:44:52