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

如何为User与Worker结构体实现通用JSON读写函数?

解决Go中重复结构体逻辑与interface{}字段访问问题

针对你遇到的User和Worker结构体重复代码、interface{}无法直接访问字段的问题,这里提供几种实用的解决方案:

1. 提取通用接口统一行为

把两个结构体的共同方法抽象成接口,让结构体各自实现接口后,就可以用接口类型编写通用逻辑,避免直接使用interface{}。

步骤示例:

首先定义通用接口:

// 认证行为接口,包含Check、SignIn方法
type Authenticatable interface {
    Check() error
    SignIn() (string, error)
}

// JSON加载接口,定义读取JSON文件的行为
type JSONLoader interface {
    LoadFromJSON(filename string) error
}

然后让User和Worker实现这些接口:

// user.go中的实现
type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // 其他User专属字段
}

func (u *User) Check() error {
    // 原有的User校验逻辑
    if u.Email == "" || u.Password == "" {
        return errors.New("用户邮箱或密码不能为空")
    }
    return nil
}

func (u *User) SignIn() (string, error) {
    if err := u.Check(); err != nil {
        return "", err
    }
    return "user-token-xxx", nil // 生成用户登录token的逻辑
}

func (u *User) LoadFromJSON(filename string) error {
    data, err := os.ReadFile(filename)
    if err != nil {
        return err
    }
    return json.Unmarshal(data, u)
}
// worker.go中的实现
type Worker struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    ID       int    `json:"id"`
    // 其他Worker专属字段
}

func (w *Worker) Check() error {
    // Worker的校验逻辑,可额外检查专属字段
    if w.Email == "" || w.Password == "" || w.ID <= 0 {
        return errors.New("Worker信息不合法")
    }
    return nil
}

func (w *Worker) SignIn() (string, error) {
    if err := w.Check(); err != nil {
        return "", err
    }
    return "worker-token-xxx", nil // 生成Worker登录token的逻辑
}

func (w *Worker) LoadFromJSON(filename string) error {
    data, err := os.ReadFile(filename)
    if err != nil {
        return err
    }
    return json.Unmarshal(data, w)
}

最后编写通用函数:

// 通用登录函数
func GenericSignIn(a Authenticatable) (string, error) {
    return a.SignIn()
}

// 通用JSON加载函数
func GenericLoadFromJSON(l JSONLoader, filename string) error {
    return l.LoadFromJSON(filename)
}

调用时直接传入结构体实例即可,不需要再用interface{},自然能正常访问方法和字段。

2. 用Go泛型实现通用逻辑(Go 1.18+)

如果两个结构体有共同字段(比如Email、Password),可以通过泛型约束来直接访问这些字段,同时复用JSON读取等通用逻辑。

步骤示例:

定义包含共同字段的泛型约束:

// 约束类型,要求类型是包含Email和Password字段的结构体
type AuthEntity interface {
    ~struct {
        Email    string
        Password string
        // 可添加其他共同字段
    }
}

编写通用校验和JSON读取函数:

// 通用校验函数,直接访问约束中的字段
func GenericCheck[T AuthEntity](entity T) error {
    if entity.Email == "" || entity.Password == "" {
        return errors.New("邮箱或密码不能为空")
    }
    return nil
}

// 通用JSON读取函数,支持任意结构体类型
func GenericReadJSON[T any](filename string) (T, error) {
    var result T
    data, err := os.ReadFile(filename)
    if err != nil {
        return result, err
    }
    err = json.Unmarshal(data, &result)
    return result, err
}

调用示例:

// 读取用户列表
users, err := GenericReadJSON[[]User]("users.json")
if err != nil {
    // 处理错误
}
for _, u := range users {
    if err := GenericCheck(u); err != nil {
        // 处理校验错误
    }
}

// 读取Worker列表
workers, err := GenericReadJSON[[]Worker]("workers.json")

3. 结构体嵌入复用基础逻辑

如果User和Worker有大量共同字段和逻辑,可以把这部分抽成基础结构体,通过嵌入的方式复用代码。

步骤示例:

定义基础Auth结构体,包含共同字段和逻辑:

// auth.go
type Auth struct {
    Email    string `json:"email"`
    Password string `json:"password"`
}

func (a *Auth) Check() error {
    if a.Email == "" || a.Password == "" {
        return errors.New("邮箱或密码不能为空")
    }
    return nil
}

func (a *Auth) GenerateBaseToken() (string, error) {
    if err := a.Check(); err != nil {
        return "", err
    }
    return fmt.Sprintf("base-token-%s", a.Email), nil
}

在User和Worker中嵌入Auth结构体:

// user.go
type User struct {
    Auth          // 嵌入基础Auth结构体
    Username string `json:"username"` // User专属字段
}

// 重写SignIn方法,添加User专属逻辑
func (u *User) SignIn() (string, error) {
    baseToken, err := u.GenerateBaseToken()
    if err != nil {
        return "", err
    }
    return fmt.Sprintf("user-%s", baseToken), nil
}
// worker.go
type Worker struct {
    Auth       // 嵌入基础Auth结构体
    ID      int `json:"id"` // Worker专属字段
}

// 重写Check方法,添加Worker专属校验
func (w *Worker) Check() error {
    // 先调用基础Auth的校验
    if err := w.Auth.Check(); err != nil {
        return err
    }
    // 检查Worker专属字段
    if w.ID <= 0 {
        return errors.New("Worker ID不合法")
    }
    return nil
}

func (w *Worker) SignIn() (string, error) {
    baseToken, err := w.GenerateBaseToken()
    if err != nil {
        return "", err
    }
    return fmt.Sprintf("worker-%d-%s", w.ID, baseToken), nil
}

这种方式既复用了基础逻辑,又能保留各自的专属字段和自定义方法,同时JSON序列化也会自动包含嵌入的字段。

内容的提问来源于stack exchange,提问作者Oshank Kashyap

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 22:17:07