
本文介绍如何通过接口参数(interface{})和指针传递,编写一个可复用的 JSON 解析函数,支持任意结构体类型,避免为每种配置类型重复编写解码逻辑。
本文介绍如何通过接口参数(`interface{}`)和指针传递,编写一个可复用的 json 解析函数,支持任意结构体类型,避免为每种配置类型重复编写解码逻辑。
在 Go 语言中,虽然不支持传统意义上的“泛型类型参数”(Go 1.18 之前),但我们可以借助 interface{} 和反射机制实现类型无关的通用逻辑。针对 JSON 配置解析这一常见需求,关键在于:解码目标必须是可寻址的指针,因为 json.Unmarshal(或 json.Decoder.Decode)需要修改传入值的字段内容。
下面是一个简洁、安全、可复用的通用解码函数:
import (
"encoding/json"
"fmt"
"os"
)
func decode(v interface{}) error {
file, err := os.Open("conf.json")
if err != nil {
return fmt.Errorf("failed to open config file: %w", err)
}
defer file.Close() // 确保文件句柄及时释放
decoder := json.NewDecoder(file)
if err := decoder.Decode(v); err != nil {
return fmt.Errorf("failed to decode JSON into %T: %w", v, err)
}
return nil
}
使用方式非常直观,只需传入对应结构体变量的地址:
// 示例 1:解析 Configuration
var cfg Configuration
if err := decode(&cfg); err != nil {
panic(err)
}
fmt.Println("Users:", cfg.Users)
// 示例 2:解析 AnotherConfiguration
var anotherCfg AnotherConfiguration
if err := decode(&anotherCfg); err != nil {
panic(err)
}
fmt.Println("Names:", anotherCfg.Names)
✅ 注意事项与最佳实践:
- 必须传入指针(如
&cfg),否则Decode会因无法修改不可寻址的值而报错(json: cannot unmarshal object into Go value of type XXX)。 - 始终检查
os.Open和decoder.Decode的返回错误,避免静默失败;示例中已将函数改为返回error,便于调用方统一处理异常。 - 使用
defer file.Close()确保资源释放,防止文件句柄泄漏。 - 函数名避免与内置函数(如
make、new)冲突——这也是原答案将make改为decode的重要原因。
该方案无需依赖外部库或复杂反射,完全符合 Go 的惯用风格:简洁、显式、安全。无论新增多少种配置结构体(如 DatabaseConfig、APIConfig),都只需定义类型并调用 decode(&instance) 即可完成解析。











