泛型解析函数必须接收指针参数,签名应为func parsejson[t any](data []byte, target *t) error,调用时须传&config或&[]user{}等地址,不可传值;http封装中需直接传target而非⌖嵌套动态字段仍需json.rawmessage二次解析。

泛型解析函数必须接收指针参数
Go 的 json.Unmarshal 要求第二个参数是可寻址的指针,泛型函数不能绕过这点。传值(如 val T)会导致解码写入副本,原始变量不变——这是最常踩的坑,且无编译错误、无 panic,只有字段始终为零值。
正确签名必须是 func ParseJSON[T any](data []byte, target *T) error,其中 *T 确保传入的是地址。调用时必须写 &config 或 &[]User{},不能写 config 或 []User{}。
- 结构体:用
&MyConfig{} - 切片:用
&[]Item{}(注意是&[],不是[]) - map:用
&map[string]string{}(同理,必须带&)
泛型约束要兼容 map 和 struct
如果只约束 T any,泛型函数能编译,但运行时对 map[string]interface{} 会失败:因为 json.Unmarshal 内部检查目标是否可寻址、是否支持反射设置,而某些动态类型在泛型擦除后可能丢失必要信息。
稳妥做法是不加额外约束,靠 json.Unmarshal 自身运行时校验;若想提前拦截明显非法类型(如 int、string),可用空接口约束:type Decodable interface{ ~map[string]interface{} | ~[]interface{} | ~struct{} },但实际意义有限——真正出错总在解码时,不是泛型实例化时。
更实用的做法是文档注明:仅支持指针指向 struct、slice、map、基本类型指针(如 *int),不支持普通值或函数类型。
HTTP GET + JSON 解析一体封装需复用 target 指针
把 HTTP 请求和 JSON 解析合在一个泛型函数里时,常见错误是把 target 二次取地址:json.Unmarshal(body, &target)。这会导致传给 Unmarshal 的是 **T,而非预期的 *T,直接 panic “invalid indirect of …”。
正确写法是直接传 target(它已经是 *T):
func GetJSON[T any](url string, target *T) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("HTTP %d", resp.StatusCode)
}
body, _ := io.ReadAll(resp.Body)
return json.Unmarshal(body, target) // ✅ 不加 &
}
- 调用示例:
GetJSON("https://api.example.com/users", &users) - 不要写
&users再包一层:GetJSON(url, &users)中的&users就是*[]User,够了 - 如果目标是单个 struct:
GetJSON(url, &user),user是User{}类型
嵌套动态字段仍需 json.RawMessage
泛型解决的是“顶层类型可变”,但无法规避 JSON 结构模糊问题。比如 API 返回的 "data" 字段可能是 object、array、string 或 null,此时即使泛型函数能接受 *T,也无法让 T 同时满足多种类型。
标准解法仍是定义字段为 Data json.RawMessage `json:"data"`,再按需二次解析:
type Response struct {
Code int `json:"code"`
Data json.RawMessage `json:"data"`
}
var resp Response
ParseJSON(body, &resp) // 泛型函数处理顶层
// 后续根据 code 或业务逻辑决定:
var users []User
json.Unmarshal(resp.Data, &users)
泛型在这里只管“读到 Response”,不管 Data 里面是什么——这才是合理分工。强行用泛型覆盖所有嵌套变化,只会让约束复杂、可读性下降、错误推迟到运行时。
真正容易被忽略的,是泛型带来的假安全感:以为写了 [T any] 就能处理一切 JSON,结果遇到 null 字段或大小写不一致的 key,照样静默失败或 panic。类型安全 ≠ 数据结构安全,该用 map[string]interface{} 过渡、该校验字段存在性、该处理 json.UnmarshalTypeError,一步都不能省。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











