上一篇文章我们学习了如何用 reflect 检查一个参数的类型。这一篇文章,咱们获得了一个结构体类型,那么我们需要探究结构体内部的结构以及其对应的值。


结构体成员迭代

上一篇文章,我们的 marshal 函数目前是长这个样子:

到这里,我们拿到了一个 struct 的 reflect.Value 变量。接下来,我们再添加几行代码,变成这个样子:

treflect.TypeNumField()
成员解析流程
fv := v.Field(i)ft := t.Field(i)reflect.Valuereflect.StructField

对于一个结构体成员,除了字段碑身类型之外,我们还要对其其他属性进行检查,这需要用到 fv 和 ft 变量的几个参数,如下文所示:

匿名成员

Go 的结构体中,支持匿名成员。针对匿名成员的处理,有好几个需要考虑的点。此处我们先略过,后文会再专门说明,因此代码如下:

不可导出成员

Go 的结构体中,共有(可导出)成员是大写字母开头的,而私有(不可导出)成员是小写字母开头的。按照 Go 的惯例,在进行 marshal / unmarshal 操作时,私有成员是不处理的,因此这些成员,我们应当过滤掉不处理。

但是有一种情况是例外的:匿名成员本身也有可能是不可导出的,这需要区分处理。所以我们把匿名成员的处理逻辑放在了前面。因此此时的代码改写为如下所示:

Go tag 解析

tag
ownerIDOwnerIDomitempty
Name
*reflect.StructField
  • 如果指定的 tag 配置非空,则分两种情况: - 都好之前有内容,那么逗号之前的数据就是 key 名称 - 逗号之前没有内容,此时用字段的名称作为 tag
  • 如果指定的 tag 配置不存在,则以字段的名称作为 tag
  • 支持获取其他参数
Pet
-

结构体成员值读取

经过了前面的过滤之后,我们到这一步,已经可以获得每个需要处理的、合法的结构体字段信息了,接下来就是获取每一个结构体成员的值。

fvreflect.Value
reflect.Kind
  • 字符串
  • 整型
  • 浮点型
  • 布尔型

至于其他类型则比较复杂,我们再进一步在后文说明。

多说无益,这一小段代码并不长,如下所示:

代码中展示了针对各种类型地取值函数:

于是,很快啊,我们的迭代函数的主体,就完成了:

验证

我们写一个简单的 Go test 函数来验证一下:

可以看到,输出内容中正确地按照 tag 中的配置,将结构体中的字段序列化为了字节流。

下一步

OK,如果读者的需求中,仅仅需要序列化基本数据类型(字符串、布尔值、数字),那么到这里为止,marshal 函数就可以算是完成了。

但是读者是否还记得我们在本文中留下了些 TODO 项,这就是我们在下一篇文章中需要处理的功能了。本文的代码也可以在 Github 上找到,本阶段的代码对应 Commit b2db350。

其他文章推荐

原文标题:《手把手教你用 reflect 包解析 Go 的结构体 - Step 2: 结构体成员遍历》

发布日期:2021-06-29