反射能获取到结构体中小写开头的字段么? | 青训营笔记

281 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

今天开例会讨论问题和排查bug的时候,有组员问为什么封了一层就拿不到数据了。

这里做一下简单复现。

代码如下,就是 Hertz 的示例代码

 import (
     "context"
     
     "github.com/cloudwego/hertz/pkg/app"
     "github.com/cloudwego/hertz/pkg/app/server"
     "github.com/cloudwego/hertz/pkg/protocol/consts"
 )
 ​
 func main() {
     h := server.Default()
     
     h.GET("/ping", func(c context.Context, rc *app.RequestContext) {
         data := []int{1, 2, 3} // 模拟数据库中查询到的数据
         rc.JSON(consts.StatusOK, data)
     })
 ​
     h.Spin()
 }

新建一个终端,然后使用curl命令做个测试(用Postman或者其他能发请求的测试工具也行)

 curl http://127.0.0.1:8888/ping

结果(已简化)

 RawContent        : HTTP/1.1 200 OK
                     Content-Length: 7
                     Content-Type: application/json; charset=utf-8
                     Date: Wed, 18 Jan 2023 15:34:18 GMT
                     Server: hertz
 
                     [1,2,3]

可以看到是能正常拿到数据的。

然后将结果封装一层。

 type result struct {
     code int
     data []int
 }
 ​
 func main() {
     h := server.Default()
     
     h.GET("/ping", func(c context.Context, rc *app.RequestContext) {
         data := []int{1, 2, 3} // 模拟数据库中查询到的数据
         res := result{code: 0, data: data}
         rc.JSON(consts.StatusOK, res)
     })
 ​
     h.Spin()
 }

然后执行同样的测试命令,结果却是

 RawContent        : HTTP/1.1 200 OK
                     Content-Length: 2
                     Content-Type: application/json; charset=utf-8
                     Date: Wed, 18 Jan 2023 15:40:27 GMT
                     Server: hertz
 
                     {}

body 变成了一个空对象。

这是一个对于初学者来说比较经典的问题了。在Go语言中,以大写开头的字段才能被其他包所访问(暂时不考虑反射),小写的字段仅在当前包下可见。

改成大写后,问题就消失了。

既然进行JSON序列化的时候会忽略命名为小写开头的字段,而JSON序列化的包一般是通过反射获取结构体中的字段。

那么究竟是反射无法获取到小写开头的字段还是能获取而不去用呢。

我们可以写个Demo来验证下。

另外新建一个包 result

 type Result struct {
     code int
     data []int
 }

code 和 data 都是小写,是私有变量,故在 result 包外无法直接访问。

在 result 包外写一个简单的反射 Demo。

 package main
 ​
 import (
     "reflect"
     "reflectDemo/result"
 )
 ​
 func main() {
     r := result.Result{}
     t := reflect.TypeOf(r)
     println(t.NumField())
     for i := 0; i < t.NumField(); i++ {
         println(t.Field(i).Name)
     }
 }

运行结果如下

 2
 code
 data

故结论是能获取(和 Java 中反射能获取到 private 修饰的字段是一个道理),但是并不会进行序列化。